Part 1: RNA

Load RNA samples

Out of 30 samples, we selected 17 for this study. These are the normal tissue samples form the control, the UVA and the UVA+SFN treatment groups. normal tissue samples from the UVB_UA groups as well as tumor samples were excluded from this analysis. Additionally, one of the control samples at Week 2 (baseline) was removed after outlier analysis.
7,219 genes with zero counts in > 80% (> 13 out of 18) of samples were removed. 17,202 out of 24,421 genes were left.

[1] 7219
[1] 17202

Transcripts per kilobase million (TPM) normalization

Next, we noramized the counts. To convert number of hits to the relative abundane of genes in each sample, we used transcripts per kilobase million (TPM) normalization, which is as following for the j-th sample:
1. normilize for gene length: a[i, j] = 1,000*count[i, j]/gene[i, j] length(bp)
2. normalize for seq depth (i.e. total count): a(i, j)/sum(a[, j])
3. multiply by one million
A very good comparison of normalization techniques can be found at the following video:
RPKM, FPKM and TPM, clearly explained

After the normalization, each sample’s total is 1M:

02w_CON_0 02w_SFN_0 02w_SFN_1 02w_UVB_0 02w_UVB_1 15w_CON_0 15w_CON_1 15w_SFN_0 15w_SFN_1 15w_UVB_0 15w_UVB_1 25w_CON_0 
    1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06     1e+06 
25w_CON_1 25w_SFN_0 25w_SFN_1 25w_UVB_0 25w_UVB_1 
    1e+06     1e+06     1e+06     1e+06     1e+06 

Color Legend:
YELLOW: TMP > 10
RED: TMP > 100

Top 100 most abundant RNA molecules

# Separate top 100 abundant genes
tmp <- droplevels(tpm[Geneid %in% levels(tpm$Geneid)[(nrow(tpm) - 99):nrow(tpm)]])

tmp <- melt.data.table(data = tmp,
                       id.vars = 1:2,
                       measure.vars = 3:ncol(tmp),
                       variable.name = "Sample",
                       value.name = "TPM")

tmp$Week <- substr(x = tmp$Sample,
                   start = 1,
                   stop = 3)
tmp$Week <- factor(tmp$Week,
                   levels = unique(tmp$Week))


tmp$Treatment <- substr(x = tmp$Sample,
                        start = 5,
                        stop = 7)
tmp$Treatment <- factor(tmp$Treatment,
                        levels = c("CON", 
                                   "UVB",
                                   "SFN"))

tmp$Replica <- substr(x = tmp$Sample,
                      start = 9,
                      stop = 9)
tmp$Replica <- factor(tmp$Replica,
                      levels = 0:1)

# Plot top 100 abundant genes
p2 <- ggplot(tmp,
             aes(x = TPM,
                 y = Geneid,
                 fill = Treatment,
                 shape = Week)) +
  # facet_wrap(~ Sex, nrow = 1) +
  geom_point(size = 3,
             alpha = 0.5) +
  geom_vline(xintercept = 1,
             linetype = "dashed")
ggplotly(p2)

Bottom 100 least abundant RNA molecules

tmp <- droplevels(tpm[Geneid %in% levels(tpm$Geneid)[1:100]])

tmp <- melt.data.table(data = tmp,
                       id.vars = 1:2,
                       measure.vars = 3:ncol(tmp),
                       variable.name = "Sample",
                       value.name = "TPM")

tmp$Week <- substr(x = tmp$Sample,
                   start = 1,
                   stop = 3)
tmp$Week <- factor(tmp$Week,
                   levels = unique(tmp$Week))


tmp$Treatment <- substr(x = tmp$Sample,
                        start = 5,
                        stop = 7)
tmp$Treatment <- factor(tmp$Treatment,
                        levels = c("CON", 
                                   "UVB",
                                   "SFN"))

tmp$Replica <- substr(x = tmp$Sample,
                      start = 9,
                      stop = 9)
tmp$Replica <- factor(tmp$Replica,
                      levels = 0:1)

# Plot top 100 abundant genes
p3 <- ggplot(tmp,
             aes(x = TPM,
                 y = Geneid,
                 fill = Treatment,
                 shape = Week)) +
  # facet_wrap(~ Sex, nrow = 1) +
  geom_point(size = 3,
             alpha = 0.5) +
  geom_vline(xintercept = 1,
             linetype = "dashed")
ggplotly(p3)

Meta data

dmeta <- data.table(Sample = colnames(dt1)[-c(1:2)])

dmeta$time <- substr(x = dmeta$Sample,
                     start = 1,
                     stop = 3)
dmeta$time <- factor(dmeta$time,
                     levels = c("02w",
                                "15w",
                                "25w"))
dmeta$Week <- factor(dmeta$time,
                     levels = c("02w",
                                "15w",
                                "25w"),
                     labels = c("Week 2",
                                "Week 15",
                                "Week 25"))

dmeta$trt <- substr(x = dmeta$Sample,
                    start = 5,
                    stop = 7)
dmeta$trt <- factor(dmeta$trt,
                    levels = c("CON", 
                               "UVB",
                               "SFN"))
dmeta$Treatment <- factor(dmeta$trt,
                          levels = c("CON", 
                                     "UVB",
                                     "SFN"),
                          labels = c("Negative Control",
                                     "Positive Control (UVB)",
                                     "Sulforaphane (SFN)"))

dmeta$Replica <- substr(x = dmeta$Sample,
                        start = 9,
                        stop = 9)
dmeta$Replica <- factor(dmeta$Replica,
                        levels = 0:1)

datatable(dmeta,
          rownames = FALSE,
          class = "cell-border stripe",
          options = list(pageLength = nrow(dmeta)))

PCA of TPM

NOTE: the distributions are skewed. To make them symmetric, log transformation is often applied. However, there is an issue of zeros. In this instance, we added a small values lambda[i] equal to 1/10 of the smallest non-zero value of i-th gene.

dm.tpm <- as.matrix(tpm[, -c(1:2), with = FALSE])
rownames(dm.tpm) <- tpm$Geneid

# # Remove 02w_CON_1 sample and redo PCA
# dm.tpm <- dm.tpm[, colnames(dm.tpm) != "02w_CON_1"]
# dmeta <- dmeta[dmeta$Sample != "02w_CON_1", ]

# Add lambdas to all values, then take a log
dm.ltpm <- t(apply(X = dm.tpm,
                      MARGIN = 1,
                      FUN = function(a) {
                        lambda <- min(a[a > 0])/10
                        log(a + lambda)
                      }))

# PCA----
m1 <- prcomp(t(dm.ltpm),
             center = TRUE,
             scale. = TRUE)

s1 <- summary(m1)
s1
Importance of components:
                           PC1     PC2     PC3      PC4      PC5      PC6      PC7      PC8      PC9     PC10     PC11    PC12
Standard deviation     66.5041 61.8206 45.2845 30.42909 28.24422 26.84136 25.01865 23.05989 22.08373 21.24391 20.87624 20.6980
Proportion of Variance  0.2571  0.2222  0.1192  0.05383  0.04637  0.04188  0.03639  0.03091  0.02835  0.02624  0.02534  0.0249
Cumulative Proportion   0.2571  0.4793  0.5985  0.65232  0.69869  0.74058  0.77696  0.80788  0.83623  0.86246  0.88780  0.9127
                           PC13     PC14     PC15     PC16      PC17
Standard deviation     20.28169 19.42403 19.14803 18.61200 2.085e-13
Proportion of Variance  0.02391  0.02193  0.02131  0.02014 0.000e+00
Cumulative Proportion   0.93662  0.95855  0.97986  1.00000 1.000e+00

Pareto chart of variance explained by principal components

imp <- data.table(PC = colnames(s1$importance),
                  Variance = 100*s1$importance[2, ],
                  Cumulative = 100*s1$importance[3, ])
imp$PC <- factor(imp$PC,
                 levels = imp$PC)
p1 <- ggplot(imp,
             aes(x = PC,
                 y = Variance)) +
  geom_bar(stat = "identity",
           fill = "grey",
           color = "black") +
  geom_line(aes(y = rescale(Cumulative,
                            to = c(min(Cumulative)*30/100,
                                   30)),
                group = rep(1, nrow(imp)))) +
  geom_point(aes(y = rescale(Cumulative,
                             to = c(min(Cumulative)*30/100,
                                    30)))) +
  scale_y_continuous("% Variance Explained",
                     breaks = seq(0, 30, by = 5),
                     labels = paste(seq(0, 30, by = 5),
                                    "%",
                                    sep = ""),
                     sec.axis = sec_axis(trans = ~.,
                                         name = "% Cumulative Variance",
                                         breaks = seq(0, 30, length.out = 5),
                                         labels = paste(seq(0, 100, length.out = 5),
                                                        "%",
                                                        sep = ""))) +
  scale_x_discrete("") +
  theme(axis.text.x = element_text(angle = 90,
                                   hjust = 1))

# Save for publication
tiff(filename = "tmp/pca_pareto.tiff",
     height = 6,
     width = 8,
     units = 'in',
     res = 600,
     compression = "lzw+p")
print(p1)
graphics.off()

print(p1)

First 3 principal components, pairwise

# Biplot while keep only the most important variables (Javier)----
# Select PC-s to pliot (PC1 & PC2)
choices <- c(1:3)

# Scores, i.e. points (df.u)
dt.scr <- data.table(m1$x[, choices])
# Add grouping variables
dt.scr$trt <- dmeta$trt
dt.scr$time <- dmeta$time
dt.scr$sample <- dmeta$Sample

# Loadings, i.e. arrows (df.v)
dt.rot <- as.data.frame(m1$rotation[, choices])
dt.rot$feat <- rownames(dt.rot)
dt.rot <- data.table(dt.rot)

# Axis labels
u.axis.labs <- paste(colnames(dt.rot)[choices], 
                     sprintf('(%0.1f%% explained var.)', 
                             100*m1$sdev[choices]^2/sum(m1$sdev^2)))

p1 <- ggplot(data = dt.scr,
             aes(x = PC1,
                 y = PC2,
                 color = trt,
                 shape = time)) +
  geom_point(size = 4,
             alpha = 0.5) +
  scale_x_continuous(u.axis.labs[1]) +
  scale_y_continuous(u.axis.labs[2]) +
  theme(legend.position = "none")
ggplotly(p1)


p2 <- ggplot(data = dt.scr,
             aes(x = PC1,
                 y = PC3,
                 color = trt,
                 shape = time)) +
  geom_point(size = 4,
             alpha = 0.5) +
  scale_x_continuous(u.axis.labs[1]) +
  scale_y_continuous(u.axis.labs[3]) +
  theme(legend.position = "none")
ggplotly(p2)


p3 <- ggplot(data = dt.scr,
             aes(x = PC2,
                 y = PC3,
                 color = trt,
                 shape = time)) +
  geom_point(size = 4,
             alpha = 0.5) +
  scale_x_continuous(u.axis.labs[2]) +
  scale_y_continuous(u.axis.labs[3]) +
  theme(legend.position = "none")
ggplotly(p3)


# Legend only
tmp <- ggplot(data = dt.scr,
             aes(x = PC1,
                 y = PC2,
                 color = trt,
                 shape = time)) +
  geom_point() +
  scale_color_discrete("Treatment") +
  scale_shape_discrete("Week")
p4 <- as_ggplot(get_legend(tmp))

# Save for publication
tiff(filename = "tmp/pca.tiff",
     height = 7,
     width = 9,
     units = 'in',
     res = 600,
     compression = "lzw+p")
grid.arrange(p1, p2, p3, p4, 
             nrow = 2)
graphics.off()

First 3 principal components, 3D

scatterplot3js(x = dt.scr$PC1, 
               y = dt.scr$PC2, 
               z = dt.scr$PC3, 
               color = as.numeric(dt.scr$trt),
               renderer = "auto",
               pch = dt.scr$sample,
               size = 0.1)

Differential expression analysis (DESeq2 pipeline)

Sources:
1. Analyzing RNA-seq data with DESeq2:Interactions
2. Bioconductor Question: DESeq2 time series analysis
We are testing a model with time*treatment interaction. The idea here is to find genes with significant interaction term. That would suggest that the gene expressiondifferences between the treatments depended on time. THere are several possible scenarios:
a. No difference between the negative control and the positive control groups at baseline, significant difference at the later time point. This will show the effect of the disease (UVB radiation, in this case).
b. Significant difference between the control groups at baseline, no difference at the later time point. Same as (a) above.
c. Differences between the positive control and the SFN-treated groups. Here, we are interested in the reversal of UVB effect. Again, the interaction term will need to be significant for the reasons described above.

# Relevel: make all comparisons with the positive control (UVB)
dmeta$trt <- factor(dmeta$trt,
                    levels = c("UVB",
                               "CON",
                               "SFN"))

dtm<- as.matrix(dt1[, dmeta$Sample,
                    with = FALSE])
rownames(dtm) <- dt1$Geneid

dds <- DESeqDataSetFromMatrix(countData = dtm, 
                              colData = dmeta,
                              ~ time + trt + time:trt)
# If all samples contain zeros, geometric means cannot be
# estimated. Change default 'type = "ratio"' to 'type = "poscounts"'.
# Type '?DESeq2::estimateSizeFactors' for more details.
dds <- estimateSizeFactors(object = dds,
                           type = "poscounts")

# Run DESeq----
dds <- DESeq(object = dds,
             # test = "LRT",
             # reduced = ~ time + trt,
             fitType = "local",
             sfType = "ratio",
             parallel = FALSE)
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
# NOTE (from DESeq help file, section Value):
# A DESeqDataSet object with results stored as metadata columns. 
# These results should accessed by calling the results function. 
# By default this will return the log2 fold changes and p-values
# for the last variable in the design formula. 
# See results for how to access results for other variables.
# In this case, the last term is the interaction term trt:time

# NOTE: 
# Likelihood ratio test (LRT) (chi-squared test) for GLM will only return 
# the results for the difference between the full and the reduced model

resultsNames(dds)
[1] "Intercept"       "time_15w_vs_02w" "time_25w_vs_02w" "trt_CON_vs_UVB"  "trt_SFN_vs_UVB"  "time15w.trtCON"  "time25w.trtCON" 
[8] "time15w.trtSFN"  "time25w.trtSFN" 
# Model matrix
mm1 <- model.matrix(~ time + trt + time:trt, dmeta)
mm1
   (Intercept) time15w time25w trtCON trtSFN time15w:trtCON time25w:trtCON time15w:trtSFN time25w:trtSFN
1            1       0       0      1      0              0              0              0              0
2            1       0       0      0      1              0              0              0              0
3            1       0       0      0      1              0              0              0              0
4            1       0       0      0      0              0              0              0              0
5            1       0       0      0      0              0              0              0              0
6            1       1       0      1      0              1              0              0              0
7            1       1       0      1      0              1              0              0              0
8            1       1       0      0      1              0              0              1              0
9            1       1       0      0      1              0              0              1              0
10           1       1       0      0      0              0              0              0              0
11           1       1       0      0      0              0              0              0              0
12           1       0       1      1      0              0              1              0              0
13           1       0       1      1      0              0              1              0              0
14           1       0       1      0      1              0              0              0              1
15           1       0       1      0      1              0              0              0              1
16           1       0       1      0      0              0              0              0              0
17           1       0       1      0      0              0              0              0              0
attr(,"assign")
[1] 0 1 1 2 2 3 3 3 3
attr(,"contrasts")
attr(,"contrasts")$time
[1] "contr.treatment"

attr(,"contrasts")$trt
[1] "contr.treatment"
head(mcols(dds))
DataFrame with 6 rows and 50 columns
                 baseMean           baseVar   allZero         dispGeneEst dispGeneIter             dispFit          dispersion
                <numeric>         <numeric> <logical>           <numeric>    <numeric>           <numeric>           <numeric>
Xkr4    0.414423785139076 0.750734393874421     FALSE               1e-08            1    2.35686251255345    6.43661011051539
Mrpl15   497.506315418383  6139.21631388383     FALSE 0.00292023552394721            6 0.00975181583387631  0.0060101698743299
Lypla1   1316.42450437205   94053.122870121     FALSE 0.00514177871417793           10  0.0074100485818535 0.00604102606581283
Tcea1    362.833336721312  2447.08771392985     FALSE               1e-08           20  0.0123515065189161 0.00715812241817593
Rgs20    412.785226796461  8337.26279018443     FALSE  0.0222228623148068            8  0.0111228088946145  0.0168637514204584
Atp6v1h  1163.12136188358  26870.2895984056     FALSE 0.00473653527254895            9 0.00743062379729061 0.00580961463958366
         dispIter dispOutlier             dispMAP        Intercept    time_15w_vs_02w     time_25w_vs_02w      trt_CON_vs_UVB
        <integer>   <logical>           <numeric>        <numeric>          <numeric>           <numeric>           <numeric>
Xkr4            8       FALSE    6.43661011051539  -2.359805612164 -0.228477588168501  -0.165844507463528  0.0598562849180821
Mrpl15          8       FALSE  0.0060101698743299 9.06594448953328 -0.137408907813809 -0.0412786898053219  -0.308591258014163
Lypla1          9       FALSE 0.00604102606581283 10.7337301130648 -0.629677974788472  -0.599280178188303  -0.305684497430534
Tcea1           7       FALSE 0.00715812241817593 8.78214921631808 -0.516217579095005  -0.446190172830842  -0.196562316500229
Rgs20          11       FALSE  0.0168637514204584 8.98928399842352 -0.547987096260501   -0.45980987283847 -0.0685634893160301
Atp6v1h         9       FALSE 0.00580961463958366 10.4068496272689 -0.491695240290437  -0.365919358337453   -0.17807000833384
             trt_SFN_vs_UVB      time15w.trtCON     time25w.trtCON     time15w.trtSFN      time25w.trtSFN       SE_Intercept
                  <numeric>           <numeric>          <numeric>          <numeric>           <numeric>          <numeric>
Xkr4        1.6582080718198    2.45478731530058   3.43262855563513  -1.67076473658365   -1.57787360996648   2.96284694407196
Mrpl15    0.199168294921519  0.0156586240981802 -0.102536901707458 -0.178149323759463 -0.0710688162246861 0.0911721029656416
Lypla1    0.179718039995711   0.281344903276623  0.348189855674569 -0.107101147581259 -0.0334225250935585 0.0832744171626043
Tcea1   -0.0830935380769627   0.309506757416714  0.476511703155704  0.271157924759699   0.104295727467573 0.0997712835011623
Rgs20     0.113854717310148 -0.0460895727086707 -0.119888249480383   0.24522833856804 -0.0021489805803274  0.140429387999271
Atp6v1h  0.0799789915431519   0.246974241692442    0.3213538709111  0.171814404854682  0.0421037362927887  0.082816070681427
        SE_time_15w_vs_02w SE_time_25w_vs_02w SE_trt_CON_vs_UVB SE_trt_SFN_vs_UVB SE_time15w.trtCON SE_time25w.trtCON
                 <numeric>          <numeric>         <numeric>         <numeric>         <numeric>         <numeric>
Xkr4      4.19009832838988   4.19009832833768  5.13171132899515  4.17888254775382  6.55361338849756   6.5053324829564
Mrpl15   0.128443851313393   0.12828481198956 0.161651924073278 0.128745553404475 0.208065870702812 0.207540172422408
Lypla1   0.118643311639213  0.118734269568226 0.145519737526484 0.117738852864755 0.188694868576427 0.188378486771387
Tcea1    0.143038313778663  0.142999047037131 0.175595965579231 0.142746566195654 0.228119616478045 0.226530264396902
Rgs20    0.199941844408924  0.199839918784151 0.244051721146729 0.198791901404911 0.317398591717306 0.316856976912824
Atp6v1h  0.117808586333953  0.117641366800501 0.144448031460997 0.117328387791251 0.187086661574303 0.186409051317023
        SE_time15w.trtSFN SE_time25w.trtSFN WaldStatistic_Intercept WaldStatistic_time_15w_vs_02w WaldStatistic_time_25w_vs_02w
                <numeric>         <numeric>               <numeric>                     <numeric>                     <numeric>
Xkr4     5.91776899533986   5.9177689953029      -0.796465580810876           -0.0545279776897975           -0.0395800991928805
Mrpl15  0.181401275197653 0.181107411065465         99.437702922678             -1.06979747499584            -0.321773787287314
Lypla1   0.16762877155602 0.167772361896891        128.895889983904             -5.30731961278428              -5.0472385130895
Tcea1   0.202476358816243 0.203686983950883         88.022814863515              -3.6089462009025             -3.12023179227889
Rgs20   0.281821816802482 0.282640507763004         64.012840378327             -2.74073242587354             -2.30089101134551
Atp6v1h  0.16627170341325  0.16643031647693        125.662199880281             -4.17367914845039             -3.11046503699663
        WaldStatistic_trt_CON_vs_UVB WaldStatistic_trt_SFN_vs_UVB WaldStatistic_time15w.trtCON WaldStatistic_time25w.trtCON
                           <numeric>                    <numeric>                    <numeric>                    <numeric>
Xkr4              0.0116640007749233            0.396806575171895            0.374570053157096            0.527663814974626
Mrpl15             -1.90898598815487             1.54699164091361            0.075258013461256           -0.494058092516009
Lypla1             -2.10063942270993             1.52641235771297             1.49100452703975             1.84835254620728
Tcea1              -1.11940109701175           -0.582105337392646             1.35677396882921             2.10352336110292
Rgs20             -0.280938355992205            0.572733177284935           -0.145210388172487           -0.378367081099077
Atp6v1h            -1.23276175197943            0.681667864434048             1.32010609208693             1.72391774240929
        WaldStatistic_time15w.trtSFN WaldStatistic_time25w.trtSFN WaldPvalue_Intercept WaldPvalue_time_15w_vs_02w
                           <numeric>                    <numeric>            <numeric>                  <numeric>
Xkr4              -0.282330171708181           -0.266633187476376    0.425761473479907          0.956514518769316
Mrpl15            -0.982073161091917            -0.39241252363216                    0          0.284710479117951
Lypla1            -0.638918644974184           -0.199213533836397                    0       1.11249010699918e-07
Tcea1               1.33920782823731            0.512039235127185                    0       0.000307443353893709
Rgs20              0.870153848805504         -0.00760322926581116                    0        0.00613024072810697
Atp6v1h             1.03333520573646             0.25298117064282                    0       2.99719777251695e-05
        WaldPvalue_time_25w_vs_02w WaldPvalue_trt_CON_vs_UVB WaldPvalue_trt_SFN_vs_UVB WaldPvalue_time15w.trtCON
                         <numeric>                 <numeric>                 <numeric>                 <numeric>
Xkr4             0.968427893548228         0.990693684883985         0.691510101678589         0.707980248270468
Mrpl15           0.747624073744977        0.0562638992384959         0.121865261334255         0.940009427107256
Lypla1        4.48241630561262e-07        0.0356726306570048         0.126907201963519         0.135960306359441
Tcea1          0.00180708779608543         0.262969063182376         0.560495730233128         0.174853041105187
Rgs20           0.0213977923150809         0.778757681035553         0.566825369916453          0.88454476429304
Atp6v1h        0.00186793007683514         0.217664665159797          0.49544899193699          0.18679959906884
        WaldPvalue_time25w.trtCON WaldPvalue_time15w.trtSFN WaldPvalue_time25w.trtSFN  betaConv  betaIter         deviance
                        <numeric>                 <numeric>                 <numeric> <logical> <numeric>        <numeric>
Xkr4            0.597732692383313         0.777690352523301         0.789751600479958      TRUE        13 25.9033824686373
Mrpl15          0.621265153192805         0.326063806588436         0.694753434090283      TRUE         2 165.306361397833
Lypla1         0.0645513587969476         0.522875857997676         0.842095713129838      TRUE         2 196.962147294101
Tcea1          0.0354200453908308         0.180503024691872         0.608623550427472      TRUE         2 157.679951768679
Rgs20           0.705157919128452         0.384216333175638         0.993933559205863      TRUE         3 178.614721232345
Atp6v1h        0.0847226938662104         0.301447057198772         0.800282763673559      TRUE         2 192.597108944526
         maxCooks
        <logical>
Xkr4           NA
Mrpl15         NA
Lypla1         NA
Tcea1          NA
Rgs20          NA
Atp6v1h        NA

Results

Effect of UVB at Week 2

# res_con_uvb_week2 <- results(dds,
#                              contrast = c(0,0,0,1,0,0,0,0,0),
#                              alpha = 0.1)
# SAME AS:
res_con_uvb_week2 <- results(dds,
                             name = "trt_CON_vs_UVB",
                             alpha = 0.1)
res_con_uvb_week2 <- res_con_uvb_week2[order(res_con_uvb_week2$padj,
                                                   decreasing = FALSE),]
summary(res_con_uvb_week2)

out of 17202 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 1546, 9%
LFC < 0 (down)     : 1537, 8.9%
outliers [1]       : 0, 0%
low counts [2]     : 2335, 14%
(mean count < 2)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# How many adjusted p-values were less than 0.05?
sum(res_con_uvb_week2$padj < 0.1, 
    na.rm = TRUE)
[1] 3083
# MA plot
# Save for publication
tiff(filename = "tmp/ma_w2_con_uvb.tiff",
     height = 6,
     width = 7,
     units = 'in',
     res = 600,
     compression = "lzw+p")
plotMA(res_con_uvb_week2,
             main = "Control vs. UVB at Week 2",
             alpha = 0.8)
graphics.off()

plotMA(res_con_uvb_week2,
             main = "Control vs. UVB at Week 2",
             alpha = 0.8)

Protective effect of SFN at Week 2

# res_sfn_uvb_week2 <- results(dds,
#                              contrast = c(0,0,0,0,1,0,0,0,0),
#                              alpha = 0.1)
# SAME AS;
res_sfn_uvb_week2 <- results(dds,
                             name = "trt_SFN_vs_UVB",
                             alpha = 0.1)
res_sfn_uvb_week2 <- res_sfn_uvb_week2[order(res_sfn_uvb_week2$padj,
                                                   decreasing = FALSE),]
summary(res_sfn_uvb_week2)

out of 17202 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 26, 0.15%
LFC < 0 (down)     : 35, 0.2%
outliers [1]       : 0, 0%
low counts [2]     : 3669, 21%
(mean count < 5)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# How many adjusted p-values were less than 0.05?
sum(res_sfn_uvb_week2$padj < 0.1, 
    na.rm = TRUE)
[1] 61
# MA plot
# Save for publication
tiff(filename = "tmp/ma_w2_sfn_uvb.tiff",
     height = 6,
     width = 7,
     units = 'in',
     res = 600,
     compression = "lzw+p")
print(plotMA(res_sfn_uvb_week2,
             main = "UVB+SFN vs UVB at Week 2",
             alpha = 0.8))
NULL
graphics.off()

print(plotMA(res_sfn_uvb_week2,
             main = "UVB+SFN vs UVB at Week 2",
             alpha = 0.8))
NULL

Genes that were significantly differentiated at both comparisons at Week 2

lgene.w2.con <- unique(res_con_uvb_week2@rownames[res_con_uvb_week2$padj < 0.1])
lgene.w2.sfn <- unique(res_sfn_uvb_week2@rownames[res_sfn_uvb_week2$padj < 0.1])
lgene.w2 <- lgene.w2.con[lgene.w2.con %in% lgene.w2.sfn]
lgene.w2 <- lgene.w2 [!is.na(lgene.w2 )]
lgene.w2
 [1] "Utrn"    "Stom"    "Tesc"    "Cited4"  "Cdhr1"   "Slc7a11" "Mki67"   "Cyp26b1" "Smc2"    "Mad2l1"  "Slc4a7"  "Ankrd23"
[13] "Ifitm3"  "Etv3"    "Pla2g4d" "Fetub"   "Kif11"   "Ccl6"    "Has3"    "Il19"    "A4galt"  "Otud1"   "Msn"     "Nqo1"   
[25] "Dbf4"    "Cblb"    "Tbc1d24" "Elmo2"   "Cd163"   "Esd"     "Rfx2"    "Gsta1"   "Slurp1"  "Arntl2"  "Vldlr"   "Tmem173"
[37] "Gpx2"    "Slfn9"   "Adh7"    "Sprr2i"  "Bcl2l15"

Plot of DESeq-normalizedcounts of genes significant in both comparisons at Week 2:

# Get the DESeq-normalize counts
dp1 <- list()
for (i in 1:length(lgene.w2)) {
  out <- plotCounts(dds, 
                    gene = lgene.w2[[i]],
                    intgroup = c("trt",
                                 "time"),
                    returnData = TRUE)
  dp1[[i]] <- data.table(Geneid = lgene.w2[[i]],
                         Sample = rownames(out),
                         out)
}
dp1 <- rbindlist(dp1)
dp1$trt <- factor(dp1$trt,
                  levels = c("CON",
                             "UVB",
                             "SFN"))
dp1$time <- factor(dp1$time,
                   levels = c("02w",
                              "15w",
                              "25w"),
                   labels = c("Week 2",
                              "Week 15",
                              "Week 25"))
dp1$Geneid <- factor(dp1$Geneid,
                     levels = lgene.w2)
dp1[, mu := mean(count,
                 na.rm = TRUE),
    by = c("Geneid",
           "trt",
           "time")]
dmu <- unique(dp1[, -c("Sample",
                       "count")])
datatable(head(dmu),
          rownames = FALSE,
          class = "cell-border stripe") %>% 
  formatRound(columns = 4,
              digits = 2)
List of 1
 $ axis.text.x:List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 1
  ..$ vjust        : NULL
  ..$ angle        : num 45
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi FALSE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE

Did down-up-down trend persist?

dp1.tmp <- dp1[dp1$Geneid %in% unique(dmu.w2$Geneid[dmu.w2$up.dn]), ]
dmu.tmp <- dmu[dmu$Geneid %in% unique(dmu.w2$Geneid[dmu.w2$up.dn]), ]
p1 <- ggplot(dp1.tmp,
             aes(x = time,
                 y = count,
                 group = trt,
                 fill = trt)) +
  facet_wrap(~ Geneid,
             scale = "free_y") +
  geom_point(position = position_dodge(0.5),
             shape = 21,
             size = 5,
             color = "black") +
  geom_line(data = dmu.tmp,
            aes(x = time,
                y = mu,
                group = trt,
                colour = trt),
            position = position_dodge(0.5),
            alpha = 0.5,
            size = 2) +
  scale_x_discrete("") +
  scale_y_continuous("DESeq-Normalized Counts") +
  scale_fill_discrete("Treatment")
print(p1)

List of 1
 $ axis.text.x:List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 1
  ..$ vjust        : NULL
  ..$ angle        : num 45
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi FALSE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE

Did up-down-up trend persist?

dp1.tmp <- dp1[dp1$Geneid %in% unique(dmu.w2$Geneid[dmu.w2$dn.up]), ]
dmu.tmp <- dmu[dmu$Geneid %in% unique(dmu.w2$Geneid[dmu.w2$dn.up]), ]
p1 <- ggplot(dp1.tmp,
             aes(x = time,
                 y = count,
                 group = trt,
                 fill = trt)) +
  facet_wrap(~ Geneid,
             scale = "free_y") +
  geom_point(position = position_dodge(0.5),
             shape = 21,
             size = 5,
             color = "black") +
  geom_line(data = dmu.tmp,
            aes(x = time,
                y = mu,
                group = trt,
                colour = trt),
            position = position_dodge(0.5),
            alpha = 0.5,
            size = 2) +
  scale_x_discrete("") +
  scale_y_continuous("DESeq-Normalized Counts") +
  scale_fill_discrete("Treatment")
print(p1)

In many of these genes, UVB+SFN moved closer to UVB over time.

Heatmap for Week 2 differentially methylated genes

up.dn.w2 <- unique(as.character(dmu.w2$Geneid[dmu.w2$up.dn]))
dn.up.w2 <- unique(as.character(dmu.w2$Geneid[dmu.w2$dn.up]))
ll <- unique(c(up.dn.w2,
               dn.up.w2))
# 36 genes

con_uvb_week2 <- data.table(Geneid = res_con_uvb_week2@rownames,
                            log2FoldChange = res_con_uvb_week2@listData$log2FoldChange)

sfn_uvb_week2 <- data.table(Geneid = res_sfn_uvb_week2@rownames,
                            log2FoldChange = -res_sfn_uvb_week2@listData$log2FoldChange)

t1 <- merge(con_uvb_week2[con_uvb_week2$Geneid %in% ll, ],
            sfn_uvb_week2[sfn_uvb_week2$Geneid %in% ll, ],
            by = "Geneid")
colnames(t1)[2:3] <- c("Control vs. UVB",
                       "UVB vs. SFN+UVB")
t1 <- t1[order(t1$`Control vs. UVB`,
               decreasing = TRUE), ]
write.csv(t1,
          file = "tmp/w2_sign_changes.csv",
          row.names = FALSE)

ll <- melt.data.table(data = t1,
                      id.vars = 1,
                      measure.vars = 2:3,
                      variable.name = "Comparison",
                      value.name = "Gene Expression Diff")
ll$Comparison <- factor(ll$Comparison,
                        levels = c("Control vs. UVB",
                                   "UVB vs. SFN+UVB"))
lvls <- ll[ll$Comparison == "Control vs. UVB", ]
ll$Geneid <- factor(ll$Geneid,
                    levels = lvls$Geneid[order(lvls$`Gene Expression Diff`)])

# Add dendrogram----
dt.dndr <- data.frame(t1[Geneid %in% levels(ll$Geneid), ])
rownames(dt.dndr) <- dt.dndr$Gene
dt.dndr <- dt.dndr[, -1]

# Compute distances between genes----
sampleDists <- dist(dt.dndr)

# Make dendrogram data----
dhc <- as.dendrogram(hclust(d = sampleDists),
                     horiz = TRUE)
ddata <- dendro_data(dhc, 
                     type = "rectangle")

# Segment data----
dtp1 <- segment(ddata)

# Hitmap data----
dtp2 <- ll
dtp2$Geneid <- factor(dtp2$Geneid,
                      levels = ddata$labels$label)

offset.size <- 4

p1 <- ggplot(data = dtp2) +
  coord_polar("y",
              start = -0.3,
              direction = -1) +
  geom_tile(aes(x =  as.numeric(Comparison),
                y = Geneid, 
                fill = `Gene Expression Diff`),
            color = "white") +
  geom_text(data = dtp2[Comparison == "Control vs. UVB", ],
            aes(x = rep(1.75,
                        nlevels(Geneid)),
                y = Geneid,
                angle = 90 + seq(from = 30,
                                 to = 330,
                                 length.out = nlevels(Geneid))[as.numeric(Geneid)] + 
                  offset.size,
                label = unique(Geneid)),
            hjust = 0) +
  geom_text(data = dtp2[Geneid == levels(dtp2$Geneid)[1], ],
            aes(x = 1:nlevels(Comparison),
                y = rep(-offset.size,
                        nlevels(Comparison)),
                angle = 0,
                label = levels(Comparison)),
            hjust = 1,
            size = 5) +
  scale_fill_gradient2(low = "red", 
                       high = "green", 
                       mid = "grey", 
                       midpoint = 0, 
                       name = "") +
  scale_y_discrete("",
                   expand = c(0, 0)) +
  theme(plot.title = element_text(hjust = 0.5),
        axis.title.x = element_blank(),
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        panel.background = element_blank(),
        legend.position = "bottom",
        legend.text = element_text(size = 15),
        legend.direction = "horizontal",
        legend.key.width = unit(1, "in"),
        legend.key.height = unit(0.3, "in")) +
  geom_segment(data = dtp1,
               aes(x = -sqrt(y) + 0.5,
                   y = x, 
                   xend = -sqrt(yend) + 0.5,
                   yend = xend),
               size = 1) 

tiff(filename = "tmp/skin_ubv_w2_sfn_hitmap_with_phylo.tiff",
     height = 8,
     width = 8,
     units = 'in',
     res = 600,
     compression = "lzw+p")
plot(p1)
graphics.off()

print(p1)

Venn Diagram, Week 2

# 1. Ctrl vs. UVB
# adjusted p-value < 0.1
# LFC > 0 (up)       : 1546, 9%
# LFC < 0 (down)     : 1537, 8.9%
# 23 genes down-up-down

# 2. SFN+UVB vs. UVB
# adjusted p-value < 0.1
# LFC > 0 (up)       : 26, 0.15%
# LFC < 0 (down)     : 35, 0.2%
# 13 gens up-down-up

p1 <- ggplot() +
  geom_circle(aes(x0 = c(1, 2, 1, 2),
                  y0 = c(1, 1, 4, 4),
                  r = rep(1, 4),
                  color = factor(c(2, 1, 1, 2))),
              size = 2) +
  geom_text(aes(x = rep(c(0.5, 1.5, 2.5), 2),
                y = rep(c(1, 4), each = 3),
                label = format(c(26, 13, 35, 1546, 23, 1537),
                               big.mark = ","))) +
  scale_color_manual(values = c("green", "red")) +
  theme_void() +
  theme(legend.position = "none")

tiff(filename = "tmp/skin_ubv_sfn_w2_venn.tiff",
     height = 6,
     width = 4,
     units = 'in',
     res = 600,
     compression = "lzw+p")
plot(p1)
graphics.off()

print(p1)

Effect of UVB at Week 15

res_con_uvb_week15 <- results(dds,
                             contrast = c(0,0,0,1,0,1,0,0,0),
                             alpha = 0.1)
# NOT THE SAME AS?!!!:
# res_con_uvb_week15 <- results(dds,
#                              contrast = list("trt_CON_vs_UVB",
#                                              "time15w.trtCON"),
#                              alpha = 0.1)

# out of 17202 with nonzero total read count
# adjusted p-value < 0.1
# LFC > 0 (up)       : 1513, 8.8%
# LFC < 0 (down)     : 1463, 8.5%
# outliers [1]       : 0, 0%
# low counts [2]     : 2668, 16%
# (mean count < 2)
# [1] see 'cooksCutoff' argument of ?results
# [2] see 'independentFiltering' argument of ?results
# 
# [1] 2976
# 
# out of 17202 with nonzero total read count
# adjusted p-value < 0.1
# LFC > 0 (up)       : 469, 2.7%
# LFC < 0 (down)     : 455, 2.6%
# outliers [1]       : 0, 0%
# low counts [2]     : 4002, 23%
# (mean count < 6)
# [1] see 'cooksCutoff' argument of ?results
# [2] see 'independentFiltering' argument of ?results
# 
# [1] 924

res_con_uvb_week15 <- res_con_uvb_week15[order(res_con_uvb_week15$padj,
                                                   decreasing = FALSE),]
summary(res_con_uvb_week15)

out of 17202 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 1513, 8.8%
LFC < 0 (down)     : 1463, 8.5%
outliers [1]       : 0, 0%
low counts [2]     : 2668, 16%
(mean count < 2)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# How many adjusted p-values were less than 0.01?
sum(res_con_uvb_week15$padj < 0.1, 
    na.rm = TRUE)
[1] 2976
# MA plot
# Save for publication
tiff(filename = "tmp/ma_w15_con_uvb.tiff",
     height = 6,
     width = 7,
     units = 'in',
     res = 600,
     compression = "lzw+p")
plotMA(res_con_uvb_week15,
             main = "Control vs. UVB at Week 15",
             alpha = 0.8)
graphics.off()

plotMA(res_con_uvb_week15,
             main = "Control vs. UVB at Week 15",
             alpha = 0.8)

Protective effect of SFN at Week 15

# res_sfn_uvb_week15 <- results(dds,
#                              contrast = c(0,0,0,0,1,0,0,1,0),
#                              alpha = 0.1)
res_sfn_uvb_week15 <- results(dds,
                             contrast = list("trt_SFN_vs_UVB",
                                             "time15w.trtSFN"),
                             alpha = 0.1)
res_sfn_uvb_week15 <- res_sfn_uvb_week15[order(res_sfn_uvb_week15$padj,
                                                   decreasing = FALSE),]
summary(res_sfn_uvb_week15)

out of 17202 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 14, 0.081%
LFC < 0 (down)     : 24, 0.14%
outliers [1]       : 0, 0%
low counts [2]     : 3335, 19%
(mean count < 4)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# How many adjusted p-values were less than 0.05?
sum(res_sfn_uvb_week15$padj < 0.1, 
    na.rm = TRUE)
[1] 38
# MA plot
# Save for publication
tiff(filename = "tmp/ma_w2_sfn_uvb.tiff",
     height = 6,
     width = 7,
     units = 'in',
     res = 600,
     compression = "lzw+p")
print(plotMA(res_sfn_uvb_week15,
             main = "UVB+SFN vs UVB at Week 15",
             alpha = 0.8))
NULL
graphics.off()

print(plotMA(res_sfn_uvb_week15,
             main = "UVB+SFN vs UVB at Week 15",
             alpha = 0.8))
NULL

Genes that were significantly differentiated at both comparisons at Week 15

lgene.w15.con <- unique(res_con_uvb_week15@rownames[res_con_uvb_week15$padj < 0.1])
lgene.w15.sfn <- unique(res_sfn_uvb_week15@rownames[res_sfn_uvb_week15$padj < 0.1])
lgene.w15 <- lgene.w15.con[lgene.w15.con %in% lgene.w15.sfn]
lgene.w15 <- lgene.w15 [!is.na(lgene.w15 )]
length(unique(lgene.w15))
[1] 15

Plot of DESeq-normalizedcounts of genes significant in both comparisons at Week 15:

# Get the DESeq-normalize counts
dp1 <- list()
for (i in 1:length(lgene.w15)) {
  out <- plotCounts(dds, 
                    gene = lgene.w15[[i]],
                    intgroup = c("trt",
                                 "time"),
                    returnData = TRUE)
  dp1[[i]] <- data.table(Geneid = lgene.w15[[i]],
                         Sample = rownames(out),
                         out)
}
dp1 <- rbindlist(dp1)
dp1$trt <- factor(dp1$trt,
                  levels = c("CON",
                             "UVB",
                             "SFN"))
dp1$time <- factor(dp1$time,
                   levels = c("02w",
                              "15w",
                              "25w"),
                   labels = c("Week 2",
                              "Week 15",
                              "Week 25"))
dp1$Geneid <- factor(dp1$Geneid,
                     levels = lgene.w15)
dp1[, mu := mean(count,
                 na.rm = TRUE),
    by = c("Geneid",
           "trt",
           "time")]
dmu <- unique(dp1[, -c("Sample",
                       "count")])
datatable(head(dmu),
          rownames = FALSE,
          class = "cell-border stripe") %>% 
  formatRound(columns = 4,
              digits = 2)
List of 1
 $ axis.text.x:List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 1
  ..$ vjust        : NULL
  ..$ angle        : num 45
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi FALSE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE

Did down-up-down trend persist?

dp1.tmp <- dp1[dp1$Geneid %in% unique(dmu.w15$Geneid[dmu.w15$up.dn]), ]
dmu.tmp <- dmu[dmu$Geneid %in% unique(dmu.w15$Geneid[dmu.w15$up.dn]), ]
p1 <- ggplot(dp1.tmp,
             aes(x = time,
                 y = count,
                 group = trt,
                 fill = trt)) +
  facet_wrap(~ Geneid,
             scale = "free_y") +
  geom_point(position = position_dodge(0.5),
             shape = 21,
             size = 5,
             color = "black") +
  geom_line(data = dmu.tmp,
            aes(x = time,
                y = mu,
                group = trt,
                colour = trt),
            position = position_dodge(0.5),
            alpha = 0.5,
            size = 2) +
  scale_x_discrete("") +
  scale_y_continuous("DESeq-Normalized Counts") +
  scale_fill_discrete("Treatment")
print(p1)

List of 1
 $ axis.text.x:List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 1
  ..$ vjust        : NULL
  ..$ angle        : num 45
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi FALSE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE

Did up-down-up trend persist?

dp1.tmp <- dp1[dp1$Geneid %in% unique(dmu.w15$Geneid[dmu.w15$dn.up]), ]
dmu.tmp <- dmu[dmu$Geneid %in% unique(dmu.w15$Geneid[dmu.w15$dn.up]), ]
p1 <- ggplot(dp1.tmp,
             aes(x = time,
                 y = count,
                 group = trt,
                 fill = trt)) +
  facet_wrap(~ Geneid,
             scale = "free_y") +
  geom_point(position = position_dodge(0.5),
             shape = 21,
             size = 5,
             color = "black") +
  geom_line(data = dmu.tmp,
            aes(x = time,
                y = mu,
                group = trt,
                colour = trt),
            position = position_dodge(0.5),
            alpha = 0.5,
            size = 2) +
  scale_x_discrete("") +
  scale_y_continuous("DESeq-Normalized Counts") +
  scale_fill_discrete("Treatment")
print(p1)

Heatmap for Week 15 differentially methylated genes

up.dn.w15 <- unique(as.character(dmu.w15$Geneid[dmu.w15$up.dn]))
dn.up.w15 <- unique(as.character(dmu.w15$Geneid[dmu.w15$dn.up]))
ll <- unique(c(up.dn.w15,
               dn.up.w15))
# 16 genes

con_uvb_week15 <- data.table(Geneid = res_con_uvb_week15@rownames,
                             log2FoldChange = res_con_uvb_week15@listData$log2FoldChange)

sfn_uvb_week15 <- data.table(Geneid = res_sfn_uvb_week15@rownames,
                             log2FoldChange = -res_sfn_uvb_week15@listData$log2FoldChange)

t1 <- merge(con_uvb_week15[con_uvb_week15$Geneid %in% ll, ],
            sfn_uvb_week15[sfn_uvb_week15$Geneid %in% ll, ],
            by = "Geneid")
colnames(t1)[2:3] <- c("Control vs. UVB",
                       "UVB vs. SFN+UVB")
t1 <- t1[order(t1$`Control vs. UVB`,
               decreasing = TRUE), ]
write.csv(t1,
          file = "tmp/w15_sign_changes.csv",
          row.names = FALSE)

ll <- melt.data.table(data = t1,
                      id.vars = 1,
                      measure.vars = 2:3,
                      variable.name = "Comparison",
                      value.name = "Gene Expression Diff")
ll$Comparison <- factor(ll$Comparison,
                        levels = c("Control vs. UVB",
                                   "UVB vs. SFN+UVB"))
lvls <- ll[ll$Comparison == "Control vs. UVB", ]
ll$Geneid <- factor(ll$Geneid,
                    levels = lvls$Geneid[order(lvls$`Gene Expression Diff`)])

# Add dendrogram----
dt.dndr <- data.frame(t1[Geneid %in% levels(ll$Geneid), ])
rownames(dt.dndr) <- dt.dndr$Gene
dt.dndr <- dt.dndr[, -1]

# Compute distances between genes----
sampleDists <- dist(dt.dndr)

# Make dendrogram data----
dhc <- as.dendrogram(hclust(d = sampleDists),
                     horiz = TRUE)
ddata <- dendro_data(dhc, 
                     type = "rectangle")

# Segment data----
dtp1 <- segment(ddata)

# Hitmap data----
dtp2 <- ll
dtp2$Geneid <- factor(dtp2$Geneid,
                      levels = ddata$labels$label)

offset.size <- 4

p1 <- ggplot(data = dtp2) +
  coord_polar("y",
              start = -0.3,
              direction = -1) +
  geom_tile(aes(x =  as.numeric(Comparison),
                y = Geneid, 
                fill = `Gene Expression Diff`),
            color = "white") +
  geom_text(data = dtp2[Comparison == "Control vs. UVB", ],
            aes(x = rep(1.75,
                        nlevels(Geneid)),
                y = Geneid,
                angle = 90 + seq(from = 30,
                                 to = 330,
                                 length.out = nlevels(Geneid))[as.numeric(Geneid)] + 
                  offset.size,
                label = unique(Geneid)),
            hjust = 0) +
  geom_text(data = dtp2[Geneid == levels(dtp2$Geneid)[1], ],
            aes(x = 1:nlevels(Comparison),
                y = rep(-offset.size,
                        nlevels(Comparison)),
                angle = 0,
                label = levels(Comparison)),
            hjust = 1,
            size = 5) +
  scale_fill_gradient2(low = "red", 
                       high = "green", 
                       mid = "grey", 
                       midpoint = 0, 
                       name = "") +
  scale_y_discrete("",
                   expand = c(0, 0)) +
  theme(plot.title = element_text(hjust = 0.5),
        axis.title.x = element_blank(),
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        panel.background = element_blank(),
        legend.position = "bottom",
        legend.text = element_text(size = 15),
        legend.direction = "horizontal",
        legend.key.width = unit(1, "in"),
        legend.key.height = unit(0.3, "in")) +
  geom_segment(data = dtp1,
               aes(x = -sqrt(y) + 0.5,
                   y = x, 
                   xend = -sqrt(yend) + 0.5,
                   yend = xend),
               size = 1) 

tiff(filename = "tmp/skin_ubv_w15_sfn_hitmap_with_phylo.tiff",
     height = 8,
     width = 8,
     units = 'in',
     res = 600,
     compression = "lzw+p")
plot(p1)
graphics.off()

print(p1)

Venn Diagram, Week 15

# 1. Ctrl vs. UVB
# adjusted p-value < 0.1
# LFC > 0 (up)       : 1449, 8.4%
# LFC < 0 (down)     : 1481, 8.6%
# 23 genes down-up-down

# 2. SFN+UVB vs. UVB
# adjusted p-value < 0.1
# LFC > 0 (up)       : 27, 0.16%
# LFC < 0 (down)     : 9, 0.052%
# 13 gens up-down-up

p1 <- ggplot() +
  geom_circle(aes(x0 = c(1, 2, 1, 2),
                  y0 = c(1, 1, 4, 4),
                  r = rep(1, 4),
                  color = factor(c(2, 1, 1, 2))),
              size = 2) +
  geom_text(aes(x = rep(c(0.5, 1.5, 2.5), 2),
                y = rep(c(1, 4), each = 3),
                label = format(c(27, 8, 9, 1449, 8, 1481),
                               big.mark = ","))) +
  scale_color_manual(values = c("green", "red")) +
  theme_void() +
  theme(legend.position = "none")

tiff(filename = "tmp/skin_ubv_sfn_w2_venn.tiff",
     height = 6,
     width = 4,
     units = 'in',
     res = 600,
     compression = "lzw+p")
plot(p1)
graphics.off()

print(p1)

Interactions terms

Tests if the effect of NOT treating with UVB vs. treating with UVB is different at Week 15 compared to Week 2:

res_int_con_uvb_week <- results(dds, 
                                name = "time15w.trtCON",
                                alpha = 0.1)
res_int_con_uvb_week <- res_int_con_uvb_week[order(res_int_con_uvb_week$padj,
                                                   decreasing = FALSE),]
print(res_int_con_uvb_week)
summary(res_int_con_uvb_week)

# How many adjusted p-values were less than 0.05?
sum(res_int_con_uvb_week$padj < 0.1, 
    na.rm = TRUE)

# MA plot
print(plotMA(res_int_con_uvb_week,
             main = "(Control vs. UVB) x TIme Interaction",
             alpha = 0.9))

Tests if the effect of treating with UVB+SFN vs. treating with UVB is different at Week 15 compared to Week 2:

res_int_sfn_uvb_week <- results(dds, 
                                name = "time15w.trtSFN",
                                alpha = 0.1)
res_int_sfn_uvb_week <- res_int_sfn_uvb_week[order(res_int_sfn_uvb_week$padj,
                                                   decreasing = FALSE),]
print(res_int_sfn_uvb_week)
summary(res_int_sfn_uvb_week)

# How many adjusted p-values were less than 0.05?
sum(res_int_sfn_uvb_week$padj < 0.1, 
    na.rm = TRUE)

# MA plot
print(plotMA(res_int_sfn_uvb_week))

# NOTE: same as 
# res <- results(dds, 
#                   alpha = 0.05)
# res <- res[order(res$padj, decreasing = FALSE),]
# res

NOTE: By default, the results(dds)* prints the results for the last level of the last term, i.e. here it was for for the interaction term SFN vs. UVB at Week 15 vs. Week 2.

Genes with both interactions being significant

lgene.con <- unique(res_int_con_uvb_week@rownames[res_int_con_uvb_week$padj < 0.1])
lgene.sfn <- unique(res_int_sfn_uvb_week@rownames[res_int_sfn_uvb_week$padj < 0.1])
lgene <- lgene.con[lgene.con %in% lgene.sfn]
lgene <- lgene[!is.na(lgene)]
lgene

Plot of DESeq-normalizedcounts of genes with smallest adjusted p-value for the interaction term:

# Get the DESeq-normalize counts
dp1 <- list()
for (i in 1:length(lgene)) {
  out <- plotCounts(dds, 
                    gene = lgene[[i]],
                    intgroup = c("trt",
                                 "time"),
                    returnData = TRUE)
  dp1[[i]] <- data.table(Geneid = lgene[[i]],
                         Sample = rownames(out),
                         out)
}
dp1 <- rbindlist(dp1)
dp1$trt <- factor(dp1$trt,
                  levels = c("CON",
                             "UVB",
                             "SFN"))
dp1$time <- factor(dp1$time,
                   levels = c("02w",
                              "15w"),
                   labels = c("Week 2",
                              "Week 15"))
dp1$Geneid <- factor(dp1$Geneid,
                     levels = lgene)
dp1[, mu := mean(count,
                 na.rm = TRUE),
    by = c("Geneid",
           "trt",
           "time")]
dmu <- unique(dp1[, -c("Sample",
                       "count")])

p1 <- ggplot(dp1,
             aes(x = time,
                 y = count,
                 group = trt,
                 fill = trt)) +
  facet_wrap(~ Geneid,
             scale = "free_y") +
  geom_point(position = position_dodge(0.5),
             shape = 21,
             size = 5,
             color = "black") +
  geom_line(data = dmu,
            aes(x = time,
                y = mu,
                group = trt,
                colour = trt),
            position = position_dodge(0.5),
            alpha = 0.5,
            size = 2) +
  scale_x_discrete("") +
  scale_y_continuous("DESeq-Normalized Counts") +
  scale_fill_discrete("Treatment")
print(p1)

Compare to the plot of TPM-normalizedcounts of genes with smallest adjusted p-value for the interaction term:

# Examine TPM values for the same genes
tmp <- tpm[Geneid %in% lgene, ]
tmp$Geneid <- factor(tmp$Geneid,
                     levels = lgene)
tmp <- melt.data.table(data = tmp,
                       id.vars = 1,
                       measure.vars = 3:ncol(tmp),
                       variable.name = "Sample",
                       value.name = "TPM")
tmp <- merge(dmeta,
             tmp,
             by = "Sample")

p1 <- ggplot(tmp,
             aes(x = Week,
                 y = TPM,
                 fill = Treatment,
                 group = Treatment)) +
  facet_wrap(~ Geneid,
             scales = "free_y") +
  geom_point(position = position_dodge(0.5),
             shape = 21,
             size = 5,
             color = "black")+
  scale_x_discrete("")
plot(p1)

Session Information

sessionInfo()
LS0tDQp0aXRsZTogIlNraW4gVVZCIFNLSDEgbW91c2UgbW9kZWwgdHJlYXRlZCB3aXRoIFNGTiAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCiMgUGFydCAxOiBSTkENCmBgYHtyIGhlYWRlciwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yID0gRkFMU0UsIHdhcm5pbmcgID1GQUxTRX0NCiMgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiMgICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoIkRFU2VxMiIpDQoNCnJlcXVpcmUoa25pdHIpDQpyZXF1aXJlKGRhdGEudGFibGUpDQpyZXF1aXJlKERUKQ0KcmVxdWlyZShERVNlcTIpDQpyZXF1aXJlKHJlYWR4bCkNCnJlcXVpcmUoQmlvY1BhcmFsbGVsKQ0KcmVxdWlyZShnZ3Bsb3QyKQ0KcmVxdWlyZShwbG90bHkpDQpyZXF1aXJlKHRocmVlanMpDQpyZXF1aXJlKHNjYWxlcykNCnJlcXVpcmUoZ3JpZEV4dHJhKQ0KcmVxdWlyZShnZ3B1YnIpDQpyZXF1aXJlKGdnZGVuZHJvKQ0KcmVxdWlyZShnZ2ZvcmNlKQ0KDQojIE5PVEU6IG9uIERFU2VxMiBPdXRwdXQ6ICdiYXNlTWVhbicgaXMgdGhlIGF2ZXJhZ2Ugb2YgdGhlIG5vcm1hbGl6ZWQgY291bnQgdmFsdWVzLCANCiMgZGl2aWRlZCBieSB0aGUgc2l6ZSBmYWN0b3JzLCB0YWtlbiBvdmVyIGFsbCBzYW1wbGVzIGluIHRoZSBERVNlcURhdGFTZXQNCmBgYA0KDQojIyBMb2FkIFJOQSBzYW1wbGVzDQpPdXQgb2YgMzAgc2FtcGxlcywgd2Ugc2VsZWN0ZWQgMTcgZm9yIHRoaXMgc3R1ZHkuIFRoZXNlIGFyZSB0aGUgbm9ybWFsIHRpc3N1ZSBzYW1wbGVzIGZvcm0gdGhlIGNvbnRyb2wsIHRoZSBVVkEgYW5kIHRoZSBVVkErU0ZOIHRyZWF0bWVudCBncm91cHMuIG5vcm1hbCB0aXNzdWUgc2FtcGxlcyBmcm9tIHRoZSBVVkJfVUEgZ3JvdXBzIGFzIHdlbGwgYXMgdHVtb3Igc2FtcGxlcyB3ZXJlIGV4Y2x1ZGVkIGZyb20gdGhpcyBhbmFseXNpcy4gQWRkaXRpb25hbGx5LCBvbmUgb2YgdGhlIGNvbnRyb2wgc2FtcGxlcyBhdCBXZWVrIDIgKGJhc2VsaW5lKSB3YXMgcmVtb3ZlZCBhZnRlciBvdXRsaWVyIGFuYWx5c2lzLiAgICANCjcsMjE5IGdlbmVzIHdpdGggemVybyBjb3VudHMgaW4gPiA4MCUgKD4gMTMgb3V0IG9mIDE4KSBvZiBzYW1wbGVzIHdlcmUgcmVtb3ZlZC4gMTcsMjAyIG91dCBvZiAyNCw0MjEgZ2VuZXMgd2VyZSBsZWZ0LiANCiAgICAgICAgIA0KYGBge3IgZGF0YV9ybmEsIHdhcm5pbmcgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQojIExvYWQgZGF0YS0tLS0NCmR0MCA8LSBmcmVhZCgiZGF0YS9yZW55aV9kZWR1cF9ybmFzZXFfZGF0YS9mZWF0dXJlc2NvdW50c191dmItc2tpbl9kZWR1cF9yZW55aV8yLTktMjAxOC5jc3YiLA0KICAgICAgICAgICAgIHNraXAgPSAxKQ0KDQojIFJlbW92ZSB1bnVzZWQgY29sdW1ucy0tLS0NCmR0MSA8LSBkdDBbLCBjKDEsIDY6bmNvbChkdDApKSwgd2l0aCA9IEZBTFNFXQ0KDQpjbmFtZXMgPC0gY29sbmFtZXMoZHQxKVstYygxOjIpXQ0KY25hbWVzIDwtIGdzdWIoeCA9IGNuYW1lcywNCiAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiLmRlZHVwLmJhbSIsDQogICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICIiKQ0KY29sbmFtZXMoZHQxKVstYygxOjIpXSA8LSBjbmFtZXMNCg0KIyBBVFRFTlRJT04hIEluIHRoaXMgYW5hbHlzaXMsIHdlIHdpbGwgb25seSBleGFtaW5lIGNvbnRyb2xzIGFuZCBTRk4NCiMgQWxzbywgcmVtb3ZlZCBjYW5jZXIgY2VsbCBzYW1wbGVzDQp0bmFtZXMgPC0gc3Vic3RyKHggPSBjb2xuYW1lcyhkdDEpLCANCiAgICAgICAgICAgICAgICAgc3RhcnQgPSAzLA0KICAgICAgICAgICAgICAgICBzdG9wID0gMykNCg0KZ25hbWVzIDwtIHN1YnN0cih4ID0gY29sbmFtZXMoZHQxKSwgDQogICAgICAgICAgICAgICAgIHN0YXJ0ID0gNSwNCiAgICAgICAgICAgICAgICAgc3RvcCA9IDcpDQoNCmR0MSA8LSBkdDFbLCBnbmFtZXMgJWluJSBjKCJpZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidGgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNPTiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRk4iICkgJg0KICAgICAgICAgICAgIHRuYW1lcyAhPSAidCIsDQogICAgICAgICAgIHdpdGggPSBGQUxTRV0NCiMgMTggc2FtcGxlcyBsZWZ0DQoNCiMgUmVtb3ZlIHNhbXBsZSAnMDJ3X0NPTl8xJyBhcyBhbiBvdXRsaWVyDQojIFNlZSAnc2tpbl91dmJfc2ZuX2V4Y2x1ZGVfY29uMncxX3YxJyBmb3IgZGV0YWlscw0KZHQxIDwtIGR0MVssIGNvbG5hbWVzKGR0MSkgIT0gIjAyd19DT05fMSIsIHdpdGggPSBGQUxTRV0NCg0KIyBSZW1vdmUgZ2VuZXMgd2l0aCB6ZXJvIGNvdW50cyBpbiA+IDgwJSAoPiAxMyBvdXQgb2YgMTcpIG9mIHNhbXBsZXMNCnRtcCA8LSBkdDFbLCAtYygxOjIpXSA9PSAwDQp0bXAgPC0gcm93U3Vtcyh0bXApID4gMTMNCnN1bSh0bXApDQoNCmR0MSA8LSBkcm9wbGV2ZWxzKGR0MVshdG1wLCBdKQ0KbnJvdyhkdDEpDQojIDE3LDIwMiBvdXQgb2YgMjQsNDIxIGdlbmVzIGxlZnQNCg0KZGF0YXRhYmxlKGhlYWQoZHQxLCAxMCksDQogICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBjbGFzcyA9ICJjZWxsLWJvcmRlciBzdHJpcGUiLA0KICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCksDQogICAgICAgICAgY2FwdGlvbiA9ICJUYWJsZSAxOiBmaXJzdCAxMCByb3dzIG9mIHRoZSBjb3VudCB0YWJsZSIpDQpgYGANCg0KIyMgVHJhbnNjcmlwdHMgcGVyIGtpbG9iYXNlIG1pbGxpb24gKFRQTSkgbm9ybWFsaXphdGlvbg0KTmV4dCwgd2Ugbm9yYW1pemVkIHRoZSBjb3VudHMuIFRvIGNvbnZlcnQgbnVtYmVyIG9mIGhpdHMgdG8gIHRoZSByZWxhdGl2ZSBhYnVuZGFuZSBvZiBnZW5lcyBpbiBlYWNoIHNhbXBsZSwgd2UgdXNlZCAqKip0cmFuc2NyaXB0cyBwZXIga2lsb2Jhc2UgbWlsbGlvbiAoVFBNKSoqKiBub3JtYWxpemF0aW9uLCB3aGljaCBpcyBhcyBmb2xsb3dpbmcgZm9yIHRoZSBqLXRoIHNhbXBsZTogICAgICAgDQoxLiBub3JtaWxpemUgZm9yIGdlbmUgbGVuZ3RoOiBhW2ksIGpdID0gMSwwMDAqY291bnRbaSwgal0vZ2VuZVtpLCBqXSBsZW5ndGgoYnApICAgICANCjIuIG5vcm1hbGl6ZSBmb3Igc2VxIGRlcHRoIChpLmUuIHRvdGFsIGNvdW50KTogYShpLCBqKS9zdW0oYVssIGpdKSAgICAgDQozLiBtdWx0aXBseSBieSBvbmUgbWlsbGlvbiAgICAgDQpBIHZlcnkgZ29vZCBjb21wYXJpc29uIG9mIG5vcm1hbGl6YXRpb24gdGVjaG5pcXVlcyBjYW4gYmUgZm91bmQgYXQgdGhlIGZvbGxvd2luZyB2aWRlbzogICAgDQpbUlBLTSwgRlBLTSBhbmQgVFBNLCBjbGVhcmx5IGV4cGxhaW5lZF0oaHR0cHM6Ly93d3cucm5hLXNlcWJsb2cuY29tL3Jwa20tZnBrbS1hbmQtdHBtLWNsZWFybHktZXhwbGFpbmVkLykNCiAgICAgDQpBZnRlciB0aGUgbm9ybWFsaXphdGlvbiwgZWFjaCBzYW1wbGUncyB0b3RhbCBpcyAxTToNCiAgICAgDQpgYGB7ciB0cG0sIHdhcm5pbmcgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQojIE5vcm1hbGl6ZSBjb3VudHMgdG8gVFBNDQp0bXAgPC0gMTAwMCpkdDFbLCAzOm5jb2woZHQxKV0vZHQxJExlbmd0aA0KdHBtIDwtIGRhdGEudGFibGUoR2VuZWlkID0gZHQxJEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgIExlbmd0aCA9IGR0MSRMZW5ndGgsDQogICAgICAgICAgICAgICAgICBhcHBseSh0bXAsDQogICAgICAgICAgICAgICAgICAgICAgICAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oYSkgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAxMF42KihhL3N1bShhKSkNCiAgICAgICAgICAgICAgICAgICAgICAgIH0pKQ0KY29sU3Vtcyh0cG1bLCAtYygxOjIpXSkNCg0KZGF0YXRhYmxlKGhlYWQodHBtLCAxMCksDQogICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBjbGFzcyA9ICJjZWxsLWJvcmRlciBzdHJpcGUiLA0KICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCksDQogICAgICAgICAgY2FwdGlvbiA9ICJUYWJsZSAyOiB0cmFuc2NyaXB0cyBwZXIga2lsb2Jhc2UgbWlsbGlvbiAoVFBNKSBub3JtYWxpemVkIGNvdW50cyIpICU+JSANCiAgZm9ybWF0Um91bmQoY29sdW1ucyA9IDM6bmNvbCh0cG0pLA0KICAgICAgICAgICAgICBkaWdpdHMgPSAyKSAlPiUNCiAgZm9ybWF0U3R5bGUoY29sdW1ucyA9IDM6bmNvbCh0cG0pLA0KICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgIGJhY2tncm91bmRDb2xvciA9IHN0eWxlSW50ZXJ2YWwoY3V0cyA9IGMoMTAsIDEwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygid2hpdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInllbGxvdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVkIikpKQ0KIyBUb3RhbCBUUE0NCnRvdGFsIDwtIHJvd1N1bXModHBtWywgMzpuY29sKHRwbSldKQ0KDQojIFNvcnQgZ2VuZXMgYnkgcmVsYXRpdmUgYWJ1bmRhbmN5DQp0cG0kR2VuZWlkIDwtIGZhY3Rvcih0cG0kR2VuZWlkICwNCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHRwbSRHZW5laWRbb3JkZXIodG90YWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY3JlYXNpbmcgPSBGQUxTRSldKQ0KYGBgDQoNCkNvbG9yIExlZ2VuZDogICAgDQoqKllFTExPVyoqOiBUTVAgPiAxMCAgICAgIA0KKipSRUQqKjogVE1QID4gMTAwICAgIA0KDQojIFRvcCAxMDAgbW9zdCBhYnVuZGFudCBSTkEgbW9sZWN1bGVzDQpgYGB7ciBtb3N0X2FidW5kYW50fQ0KIyBTZXBhcmF0ZSB0b3AgMTAwIGFidW5kYW50IGdlbmVzDQp0bXAgPC0gZHJvcGxldmVscyh0cG1bR2VuZWlkICVpbiUgbGV2ZWxzKHRwbSRHZW5laWQpWyhucm93KHRwbSkgLSA5OSk6bnJvdyh0cG0pXV0pDQoNCnRtcCA8LSBtZWx0LmRhdGEudGFibGUoZGF0YSA9IHRtcCwNCiAgICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9IDE6MiwNCiAgICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZS52YXJzID0gMzpuY29sKHRtcCksDQogICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiU2FtcGxlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUubmFtZSA9ICJUUE0iKQ0KDQp0bXAkV2VlayA8LSBzdWJzdHIoeCA9IHRtcCRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgc3RhcnQgPSAxLA0KICAgICAgICAgICAgICAgICAgIHN0b3AgPSAzKQ0KdG1wJFdlZWsgPC0gZmFjdG9yKHRtcCRXZWVrLA0KICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHVuaXF1ZSh0bXAkV2VlaykpDQoNCg0KdG1wJFRyZWF0bWVudCA8LSBzdWJzdHIoeCA9IHRtcCRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgICBzdGFydCA9IDUsDQogICAgICAgICAgICAgICAgICAgICAgICBzdG9wID0gNykNCnRtcCRUcmVhdG1lbnQgPC0gZmFjdG9yKHRtcCRUcmVhdG1lbnQsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJDT04iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRk4iKSkNCg0KdG1wJFJlcGxpY2EgPC0gc3Vic3RyKHggPSB0bXAkU2FtcGxlLA0KICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gOSwNCiAgICAgICAgICAgICAgICAgICAgICBzdG9wID0gOSkNCnRtcCRSZXBsaWNhIDwtIGZhY3Rvcih0bXAkUmVwbGljYSwNCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAwOjEpDQoNCiMgUGxvdCB0b3AgMTAwIGFidW5kYW50IGdlbmVzDQpwMiA8LSBnZ3Bsb3QodG1wLA0KICAgICAgICAgICAgIGFlcyh4ID0gVFBNLA0KICAgICAgICAgICAgICAgICB5ID0gR2VuZWlkLA0KICAgICAgICAgICAgICAgICBmaWxsID0gVHJlYXRtZW50LA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IFdlZWspKSArDQogICMgZmFjZXRfd3JhcCh+IFNleCwgbnJvdyA9IDEpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMywNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpDQpnZ3Bsb3RseShwMikNCmBgYA0KDQojIEJvdHRvbSAxMDAgbGVhc3QgYWJ1bmRhbnQgUk5BIG1vbGVjdWxlcw0KYGBge3IgbGVhc3RfYWJ1bmRhbnR9DQp0bXAgPC0gZHJvcGxldmVscyh0cG1bR2VuZWlkICVpbiUgbGV2ZWxzKHRwbSRHZW5laWQpWzE6MTAwXV0pDQoNCnRtcCA8LSBtZWx0LmRhdGEudGFibGUoZGF0YSA9IHRtcCwNCiAgICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9IDE6MiwNCiAgICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZS52YXJzID0gMzpuY29sKHRtcCksDQogICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiU2FtcGxlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUubmFtZSA9ICJUUE0iKQ0KDQp0bXAkV2VlayA8LSBzdWJzdHIoeCA9IHRtcCRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgc3RhcnQgPSAxLA0KICAgICAgICAgICAgICAgICAgIHN0b3AgPSAzKQ0KdG1wJFdlZWsgPC0gZmFjdG9yKHRtcCRXZWVrLA0KICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHVuaXF1ZSh0bXAkV2VlaykpDQoNCg0KdG1wJFRyZWF0bWVudCA8LSBzdWJzdHIoeCA9IHRtcCRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgICBzdGFydCA9IDUsDQogICAgICAgICAgICAgICAgICAgICAgICBzdG9wID0gNykNCnRtcCRUcmVhdG1lbnQgPC0gZmFjdG9yKHRtcCRUcmVhdG1lbnQsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJDT04iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRk4iKSkNCg0KdG1wJFJlcGxpY2EgPC0gc3Vic3RyKHggPSB0bXAkU2FtcGxlLA0KICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gOSwNCiAgICAgICAgICAgICAgICAgICAgICBzdG9wID0gOSkNCnRtcCRSZXBsaWNhIDwtIGZhY3Rvcih0bXAkUmVwbGljYSwNCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAwOjEpDQoNCiMgUGxvdCB0b3AgMTAwIGFidW5kYW50IGdlbmVzDQpwMyA8LSBnZ3Bsb3QodG1wLA0KICAgICAgICAgICAgIGFlcyh4ID0gVFBNLA0KICAgICAgICAgICAgICAgICB5ID0gR2VuZWlkLA0KICAgICAgICAgICAgICAgICBmaWxsID0gVHJlYXRtZW50LA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IFdlZWspKSArDQogICMgZmFjZXRfd3JhcCh+IFNleCwgbnJvdyA9IDEpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMywNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpDQpnZ3Bsb3RseShwMykNCmBgYA0KDQojIE1ldGEgZGF0YQ0KYGBge3IgbWV0YX0NCmRtZXRhIDwtIGRhdGEudGFibGUoU2FtcGxlID0gY29sbmFtZXMoZHQxKVstYygxOjIpXSkNCg0KZG1ldGEkdGltZSA8LSBzdWJzdHIoeCA9IGRtZXRhJFNhbXBsZSwNCiAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gMSwNCiAgICAgICAgICAgICAgICAgICAgIHN0b3AgPSAzKQ0KZG1ldGEkdGltZSA8LSBmYWN0b3IoZG1ldGEkdGltZSwNCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIjAydyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxNXciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjV3IikpDQpkbWV0YSRXZWVrIDwtIGZhY3RvcihkbWV0YSR0aW1lLA0KICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiMDJ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjE1dyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyNXciKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIldlZWsgMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXZWVrIDE1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldlZWsgMjUiKSkNCg0KZG1ldGEkdHJ0IDwtIHN1YnN0cih4ID0gZG1ldGEkU2FtcGxlLA0KICAgICAgICAgICAgICAgICAgICBzdGFydCA9IDUsDQogICAgICAgICAgICAgICAgICAgIHN0b3AgPSA3KQ0KZG1ldGEkdHJ0IDwtIGZhY3RvcihkbWV0YSR0cnQsDQogICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkNPTiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVVkIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRk4iKSkNCmRtZXRhJFRyZWF0bWVudCA8LSBmYWN0b3IoZG1ldGEkdHJ0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJDT04iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0ZOIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk5lZ2F0aXZlIENvbnRyb2wiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb3NpdGl2ZSBDb250cm9sIChVVkIpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3VsZm9yYXBoYW5lIChTRk4pIikpDQoNCmRtZXRhJFJlcGxpY2EgPC0gc3Vic3RyKHggPSBkbWV0YSRTYW1wbGUsDQogICAgICAgICAgICAgICAgICAgICAgICBzdGFydCA9IDksDQogICAgICAgICAgICAgICAgICAgICAgICBzdG9wID0gOSkNCmRtZXRhJFJlcGxpY2EgPC0gZmFjdG9yKGRtZXRhJFJlcGxpY2EsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAwOjEpDQoNCmRhdGF0YWJsZShkbWV0YSwNCiAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLA0KICAgICAgICAgIGNsYXNzID0gImNlbGwtYm9yZGVyIHN0cmlwZSIsDQogICAgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IG5yb3coZG1ldGEpKSkNCmBgYA0KDQojIFBDQSBvZiBUUE0NCk5PVEU6IHRoZSBkaXN0cmlidXRpb25zIGFyZSBza2V3ZWQuIFRvIG1ha2UgdGhlbSBzeW1tZXRyaWMsIGxvZyB0cmFuc2Zvcm1hdGlvbiBpcyBvZnRlbiBhcHBsaWVkLiBIb3dldmVyLCB0aGVyZSBpcyBhbiBpc3N1ZSBvZiB6ZXJvcy4gSW4gdGhpcyBpbnN0YW5jZSwgd2UgYWRkZWQgYSBzbWFsbCB2YWx1ZXMgKioqbGFtYmRhW2ldKioqIGVxdWFsIHRvIDEvMTAgb2YgdGhlIHNtYWxsZXN0IG5vbi16ZXJvIHZhbHVlIG9mICppKi10aCBnZW5lLiANCmBgYHtyIHBjYX0NCmRtLnRwbSA8LSBhcy5tYXRyaXgodHBtWywgLWMoMToyKSwgd2l0aCA9IEZBTFNFXSkNCnJvd25hbWVzKGRtLnRwbSkgPC0gdHBtJEdlbmVpZA0KDQojICMgUmVtb3ZlIDAyd19DT05fMSBzYW1wbGUgYW5kIHJlZG8gUENBDQojIGRtLnRwbSA8LSBkbS50cG1bLCBjb2xuYW1lcyhkbS50cG0pICE9ICIwMndfQ09OXzEiXQ0KIyBkbWV0YSA8LSBkbWV0YVtkbWV0YSRTYW1wbGUgIT0gIjAyd19DT05fMSIsIF0NCg0KIyBBZGQgbGFtYmRhcyB0byBhbGwgdmFsdWVzLCB0aGVuIHRha2UgYSBsb2cNCmRtLmx0cG0gPC0gdChhcHBseShYID0gZG0udHBtLA0KICAgICAgICAgICAgICAgICAgICAgIE1BUkdJTiA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oYSkgew0KICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhIDwtIG1pbihhW2EgPiAwXSkvMTANCiAgICAgICAgICAgICAgICAgICAgICAgIGxvZyhhICsgbGFtYmRhKQ0KICAgICAgICAgICAgICAgICAgICAgIH0pKQ0KDQojIFBDQS0tLS0NCm0xIDwtIHByY29tcCh0KGRtLmx0cG0pLA0KICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsDQogICAgICAgICAgICAgc2NhbGUuID0gVFJVRSkNCg0KczEgPC0gc3VtbWFyeShtMSkNCnMxDQpgYGANCg0KIyBQYXJldG8gY2hhcnQgb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHByaW5jaXBhbCBjb21wb25lbnRzDQpgYGB7ciBwY2FfdmFyX3Bsb3R9DQppbXAgPC0gZGF0YS50YWJsZShQQyA9IGNvbG5hbWVzKHMxJGltcG9ydGFuY2UpLA0KICAgICAgICAgICAgICAgICAgVmFyaWFuY2UgPSAxMDAqczEkaW1wb3J0YW5jZVsyLCBdLA0KICAgICAgICAgICAgICAgICAgQ3VtdWxhdGl2ZSA9IDEwMCpzMSRpbXBvcnRhbmNlWzMsIF0pDQppbXAkUEMgPC0gZmFjdG9yKGltcCRQQywNCiAgICAgICAgICAgICAgICAgbGV2ZWxzID0gaW1wJFBDKQ0KcDEgPC0gZ2dwbG90KGltcCwNCiAgICAgICAgICAgICBhZXMoeCA9IFBDLA0KICAgICAgICAgICAgICAgICB5ID0gVmFyaWFuY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLA0KICAgICAgICAgICBmaWxsID0gImdyZXkiLA0KICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcmVzY2FsZShDdW11bGF0aXZlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gYyhtaW4oQ3VtdWxhdGl2ZSkqMzAvMTAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzMCkpLA0KICAgICAgICAgICAgICAgIGdyb3VwID0gcmVwKDEsIG5yb3coaW1wKSkpKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSByZXNjYWxlKEN1bXVsYXRpdmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gYyhtaW4oQ3VtdWxhdGl2ZSkqMzAvMTAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMzApKSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKCIlIFZhcmlhbmNlIEV4cGxhaW5lZCIsDQogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMzAsIGJ5ID0gNSksDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZShzZXEoMCwgMzAsIGJ5ID0gNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIiksDQogICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcyA9IHNlY19heGlzKHRyYW5zID0gfi4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiJSBDdW11bGF0aXZlIFZhcmlhbmNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDMwLCBsZW5ndGgub3V0ID0gNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHBhc3RlKHNlcSgwLCAxMDAsIGxlbmd0aC5vdXQgPSA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikpKSArDQogIHNjYWxlX3hfZGlzY3JldGUoIiIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAxKSkNCg0KIyBTYXZlIGZvciBwdWJsaWNhdGlvbg0KdGlmZihmaWxlbmFtZSA9ICJ0bXAvcGNhX3BhcmV0by50aWZmIiwNCiAgICAgaGVpZ2h0ID0gNiwNCiAgICAgd2lkdGggPSA4LA0KICAgICB1bml0cyA9ICdpbicsDQogICAgIHJlcyA9IDYwMCwNCiAgICAgY29tcHJlc3Npb24gPSAibHp3K3AiKQ0KcHJpbnQocDEpDQpncmFwaGljcy5vZmYoKQ0KDQpwcmludChwMSkNCmBgYA0KDQojIEZpcnN0IDMgcHJpbmNpcGFsIGNvbXBvbmVudHMsIHBhaXJ3aXNlDQpgYGB7ciBwY2FfcGxvdHN9DQojIEJpcGxvdCB3aGlsZSBrZWVwIG9ubHkgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlcyAoSmF2aWVyKS0tLS0NCiMgU2VsZWN0IFBDLXMgdG8gcGxpb3QgKFBDMSAmIFBDMikNCmNob2ljZXMgPC0gYygxOjMpDQoNCiMgU2NvcmVzLCBpLmUuIHBvaW50cyAoZGYudSkNCmR0LnNjciA8LSBkYXRhLnRhYmxlKG0xJHhbLCBjaG9pY2VzXSkNCiMgQWRkIGdyb3VwaW5nIHZhcmlhYmxlcw0KZHQuc2NyJHRydCA8LSBkbWV0YSR0cnQNCmR0LnNjciR0aW1lIDwtIGRtZXRhJHRpbWUNCmR0LnNjciRzYW1wbGUgPC0gZG1ldGEkU2FtcGxlDQoNCiMgTG9hZGluZ3MsIGkuZS4gYXJyb3dzIChkZi52KQ0KZHQucm90IDwtIGFzLmRhdGEuZnJhbWUobTEkcm90YXRpb25bLCBjaG9pY2VzXSkNCmR0LnJvdCRmZWF0IDwtIHJvd25hbWVzKGR0LnJvdCkNCmR0LnJvdCA8LSBkYXRhLnRhYmxlKGR0LnJvdCkNCg0KIyBBeGlzIGxhYmVscw0KdS5heGlzLmxhYnMgPC0gcGFzdGUoY29sbmFtZXMoZHQucm90KVtjaG9pY2VzXSwgDQogICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCcoJTAuMWYlJSBleHBsYWluZWQgdmFyLiknLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMTAwKm0xJHNkZXZbY2hvaWNlc11eMi9zdW0obTEkc2Rldl4yKSkpDQoNCnAxIDwtIGdncGxvdChkYXRhID0gZHQuc2NyLA0KICAgICAgICAgICAgIGFlcyh4ID0gUEMxLA0KICAgICAgICAgICAgICAgICB5ID0gUEMyLA0KICAgICAgICAgICAgICAgICBjb2xvciA9IHRydCwNCiAgICAgICAgICAgICAgICAgc2hhcGUgPSB0aW1lKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSA0LA0KICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogIHNjYWxlX3hfY29udGludW91cyh1LmF4aXMubGFic1sxXSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModS5heGlzLmxhYnNbMl0pICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KZ2dwbG90bHkocDEpDQoNCnAyIDwtIGdncGxvdChkYXRhID0gZHQuc2NyLA0KICAgICAgICAgICAgIGFlcyh4ID0gUEMxLA0KICAgICAgICAgICAgICAgICB5ID0gUEMzLA0KICAgICAgICAgICAgICAgICBjb2xvciA9IHRydCwNCiAgICAgICAgICAgICAgICAgc2hhcGUgPSB0aW1lKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSA0LA0KICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogIHNjYWxlX3hfY29udGludW91cyh1LmF4aXMubGFic1sxXSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModS5heGlzLmxhYnNbM10pICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KZ2dwbG90bHkocDIpDQoNCnAzIDwtIGdncGxvdChkYXRhID0gZHQuc2NyLA0KICAgICAgICAgICAgIGFlcyh4ID0gUEMyLA0KICAgICAgICAgICAgICAgICB5ID0gUEMzLA0KICAgICAgICAgICAgICAgICBjb2xvciA9IHRydCwNCiAgICAgICAgICAgICAgICAgc2hhcGUgPSB0aW1lKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSA0LA0KICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogIHNjYWxlX3hfY29udGludW91cyh1LmF4aXMubGFic1syXSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModS5heGlzLmxhYnNbM10pICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KZ2dwbG90bHkocDMpDQoNCiMgTGVnZW5kIG9ubHkNCnRtcCA8LSBnZ3Bsb3QoZGF0YSA9IGR0LnNjciwNCiAgICAgICAgICAgICBhZXMoeCA9IFBDMSwNCiAgICAgICAgICAgICAgICAgeSA9IFBDMiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSB0cnQsDQogICAgICAgICAgICAgICAgIHNoYXBlID0gdGltZSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoIlRyZWF0bWVudCIpICsNCiAgc2NhbGVfc2hhcGVfZGlzY3JldGUoIldlZWsiKQ0KcDQgPC0gYXNfZ2dwbG90KGdldF9sZWdlbmQodG1wKSkNCg0KIyBTYXZlIGZvciBwdWJsaWNhdGlvbg0KdGlmZihmaWxlbmFtZSA9ICJ0bXAvcGNhLnRpZmYiLA0KICAgICBoZWlnaHQgPSA3LA0KICAgICB3aWR0aCA9IDksDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gNjAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgcDQsIA0KICAgICAgICAgICAgIG5yb3cgPSAyKQ0KZ3JhcGhpY3Mub2ZmKCkNCmBgYA0KDQojIEZpcnN0IDMgcHJpbmNpcGFsIGNvbXBvbmVudHMsIDNEDQpgYGB7ciBwY2FfM2QsIGZpZy5oZWlnaHQgPSAxMCwgZmlnLndpZHRoID0gMTB9DQpzY2F0dGVycGxvdDNqcyh4ID0gZHQuc2NyJFBDMSwgDQogICAgICAgICAgICAgICB5ID0gZHQuc2NyJFBDMiwgDQogICAgICAgICAgICAgICB6ID0gZHQuc2NyJFBDMywgDQogICAgICAgICAgICAgICBjb2xvciA9IGFzLm51bWVyaWMoZHQuc2NyJHRydCksDQogICAgICAgICAgICAgICByZW5kZXJlciA9ICJhdXRvIiwNCiAgICAgICAgICAgICAgIHBjaCA9IGR0LnNjciRzYW1wbGUsDQogICAgICAgICAgICAgICBzaXplID0gMC4xKQ0KYGBgDQoNCiMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgKERFU2VxMiBwaXBlbGluZSkNClNvdXJjZXM6ICAgIA0KMS4gW0FuYWx5emluZyBSTkEtc2VxIGRhdGEgd2l0aCBERVNlcTI6SW50ZXJhY3Rpb25zXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNpbnRlcmFjdGlvbnMpICAgICANCjIuIFtCaW9jb25kdWN0b3IgUXVlc3Rpb246IERFU2VxMiB0aW1lIHNlcmllcyBhbmFseXNpc10oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC85NzQzMC8pICAgICAgDQpXZSBhcmUgdGVzdGluZyBhIG1vZGVsIHdpdGggdGltZSp0cmVhdG1lbnQgaW50ZXJhY3Rpb24uIFRoZSBpZGVhIGhlcmUgaXMgdG8gZmluZCBnZW5lcyB3aXRoIHNpZ25pZmljYW50IGludGVyYWN0aW9uIHRlcm0uIFRoYXQgd291bGQgc3VnZ2VzdCB0aGF0IHRoZSBnZW5lIGV4cHJlc3Npb25kaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB0cmVhdG1lbnRzIGRlcGVuZGVkIG9uIHRpbWUuIFRIZXJlIGFyZSBzZXZlcmFsIHBvc3NpYmxlIHNjZW5hcmlvczogICAgDQphLiBObyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG5lZ2F0aXZlIGNvbnRyb2wgYW5kIHRoZSBwb3NpdGl2ZSBjb250cm9sIGdyb3VwcyBhdCBiYXNlbGluZSwgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBhdCB0aGUgbGF0ZXIgdGltZSBwb2ludC4gVGhpcyB3aWxsIHNob3cgdGhlIGVmZmVjdCBvZiB0aGUgZGlzZWFzZSAoVVZCIHJhZGlhdGlvbiwgaW4gdGhpcyBjYXNlKS4gICAgIA0KYi4gU2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBjb250cm9sIGdyb3VwcyBhdCBiYXNlbGluZSwgbm8gZGlmZmVyZW5jZSBhdCB0aGUgbGF0ZXIgdGltZSBwb2ludC4gU2FtZSBhcyAoYSkgYWJvdmUuICAgICANCmMuIERpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHBvc2l0aXZlIGNvbnRyb2wgYW5kIHRoZSBTRk4tdHJlYXRlZCBncm91cHMuIEhlcmUsIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSByZXZlcnNhbCBvZiBVVkIgZWZmZWN0LiBBZ2FpbiwgdGhlIGludGVyYWN0aW9uIHRlcm0gd2lsbCBuZWVkIHRvIGJlIHNpZ25pZmljYW50IGZvciB0aGUgcmVhc29ucyBkZXNjcmliZWQgYWJvdmUuICAgICAgDQoNCmBgYHtyIGRlc2VxMn0NCiMgUmVsZXZlbDogbWFrZSBhbGwgY29tcGFyaXNvbnMgd2l0aCB0aGUgcG9zaXRpdmUgY29udHJvbCAoVVZCKQ0KZG1ldGEkdHJ0IDwtIGZhY3RvcihkbWV0YSR0cnQsDQogICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlVWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNPTiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNGTiIpKQ0KDQpkdG08LSBhcy5tYXRyaXgoZHQxWywgZG1ldGEkU2FtcGxlLA0KICAgICAgICAgICAgICAgICAgICB3aXRoID0gRkFMU0VdKQ0Kcm93bmFtZXMoZHRtKSA8LSBkdDEkR2VuZWlkDQoNCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGR0bSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gZG1ldGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+IHRpbWUgKyB0cnQgKyB0aW1lOnRydCkNCiMgSWYgYWxsIHNhbXBsZXMgY29udGFpbiB6ZXJvcywgZ2VvbWV0cmljIG1lYW5zIGNhbm5vdCBiZQ0KIyBlc3RpbWF0ZWQuIENoYW5nZSBkZWZhdWx0ICd0eXBlID0gInJhdGlvIicgdG8gJ3R5cGUgPSAicG9zY291bnRzIicuDQojIFR5cGUgJz9ERVNlcTI6OmVzdGltYXRlU2l6ZUZhY3RvcnMnIGZvciBtb3JlIGRldGFpbHMuDQpkZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhvYmplY3QgPSBkZHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInBvc2NvdW50cyIpDQoNCiMgUnVuIERFU2VxLS0tLQ0KZGRzIDwtIERFU2VxKG9iamVjdCA9IGRkcywNCiAgICAgICAgICAgICAjIHRlc3QgPSAiTFJUIiwNCiAgICAgICAgICAgICAjIHJlZHVjZWQgPSB+IHRpbWUgKyB0cnQsDQogICAgICAgICAgICAgZml0VHlwZSA9ICJsb2NhbCIsDQogICAgICAgICAgICAgc2ZUeXBlID0gInJhdGlvIiwNCiAgICAgICAgICAgICBwYXJhbGxlbCA9IEZBTFNFKQ0KDQojIE5PVEUgKGZyb20gREVTZXEgaGVscCBmaWxlLCBzZWN0aW9uIFZhbHVlKToNCiMgQSBERVNlcURhdGFTZXQgb2JqZWN0IHdpdGggcmVzdWx0cyBzdG9yZWQgYXMgbWV0YWRhdGEgY29sdW1ucy4gDQojIFRoZXNlIHJlc3VsdHMgc2hvdWxkIGFjY2Vzc2VkIGJ5IGNhbGxpbmcgdGhlIHJlc3VsdHMgZnVuY3Rpb24uIA0KIyBCeSBkZWZhdWx0IHRoaXMgd2lsbCByZXR1cm4gdGhlIGxvZzIgZm9sZCBjaGFuZ2VzIGFuZCBwLXZhbHVlcw0KIyBmb3IgdGhlIGxhc3QgdmFyaWFibGUgaW4gdGhlIGRlc2lnbiBmb3JtdWxhLiANCiMgU2VlIHJlc3VsdHMgZm9yIGhvdyB0byBhY2Nlc3MgcmVzdWx0cyBmb3Igb3RoZXIgdmFyaWFibGVzLg0KIyBJbiB0aGlzIGNhc2UsIHRoZSBsYXN0IHRlcm0gaXMgdGhlIGludGVyYWN0aW9uIHRlcm0gdHJ0OnRpbWUNCg0KIyBOT1RFOiANCiMgTGlrZWxpaG9vZCByYXRpbyB0ZXN0IChMUlQpIChjaGktc3F1YXJlZCB0ZXN0KSBmb3IgR0xNIHdpbGwgb25seSByZXR1cm4gDQojIHRoZSByZXN1bHRzIGZvciB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBmdWxsIGFuZCB0aGUgcmVkdWNlZCBtb2RlbA0KDQpyZXN1bHRzTmFtZXMoZGRzKQ0KDQojIE1vZGVsIG1hdHJpeA0KbW0xIDwtIG1vZGVsLm1hdHJpeCh+IHRpbWUgKyB0cnQgKyB0aW1lOnRydCwgZG1ldGEpDQptbTENCg0KaGVhZChtY29scyhkZHMpKQ0KYGBgDQoNCiMgUmVzdWx0cw0KIyMgRWZmZWN0IG9mIFVWQiBhdCBXZWVrIDINCmBgYHtyIGRlc2VxMl9yZXN1bHRzX3dlZWsyX2Nvbl91dmJ9DQojIHJlc19jb25fdXZiX3dlZWsyIDwtIHJlc3VsdHMoZGRzLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0ID0gYygwLDAsMCwxLDAsMCwwLDAsMCksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjEpDQojIFNBTUUgQVM6DQpyZXNfY29uX3V2Yl93ZWVrMiA8LSByZXN1bHRzKGRkcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJ0cnRfQ09OX3ZzX1VWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC4xKQ0KcmVzX2Nvbl91dmJfd2VlazIgPC0gcmVzX2Nvbl91dmJfd2VlazJbb3JkZXIocmVzX2Nvbl91dmJfd2VlazIkcGFkaiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY3JlYXNpbmcgPSBGQUxTRSksXQ0Kc3VtbWFyeShyZXNfY29uX3V2Yl93ZWVrMikNCg0KIyBIb3cgbWFueSBhZGp1c3RlZCBwLXZhbHVlcyB3ZXJlIGxlc3MgdGhhbiAwLjA1Pw0Kc3VtKHJlc19jb25fdXZiX3dlZWsyJHBhZGogPCAwLjEsIA0KICAgIG5hLnJtID0gVFJVRSkNCg0KIyBNQSBwbG90DQojIFNhdmUgZm9yIHB1YmxpY2F0aW9uDQp0aWZmKGZpbGVuYW1lID0gInRtcC9tYV93Ml9jb25fdXZiLnRpZmYiLA0KICAgICBoZWlnaHQgPSA2LA0KICAgICB3aWR0aCA9IDcsDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gNjAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpwbG90TUEocmVzX2Nvbl91dmJfd2VlazIsDQogICAgICAgICAgICAgbWFpbiA9ICJDb250cm9sIHZzLiBVVkIgYXQgV2VlayAyIiwNCiAgICAgICAgICAgICBhbHBoYSA9IDAuOCkNCmdyYXBoaWNzLm9mZigpDQoNCnBsb3RNQShyZXNfY29uX3V2Yl93ZWVrMiwNCiAgICAgICAgICAgICBtYWluID0gIkNvbnRyb2wgdnMuIFVWQiBhdCBXZWVrIDIiLA0KICAgICAgICAgICAgIGFscGhhID0gMC44KQ0KYGBgDQoNCiMjIFByb3RlY3RpdmUgZWZmZWN0IG9mIFNGTiBhdCBXZWVrIDINCmBgYHtyIGRlc2VxMl9yZXN1bHRzX3dlZWsyX3Nmbl91dmJ9DQojIHJlc19zZm5fdXZiX3dlZWsyIDwtIHJlc3VsdHMoZGRzLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0ID0gYygwLDAsMCwwLDEsMCwwLDAsMCksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjEpDQojIFNBTUUgQVM7DQpyZXNfc2ZuX3V2Yl93ZWVrMiA8LSByZXN1bHRzKGRkcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJ0cnRfU0ZOX3ZzX1VWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC4xKQ0KcmVzX3Nmbl91dmJfd2VlazIgPC0gcmVzX3Nmbl91dmJfd2VlazJbb3JkZXIocmVzX3Nmbl91dmJfd2VlazIkcGFkaiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY3JlYXNpbmcgPSBGQUxTRSksXQ0Kc3VtbWFyeShyZXNfc2ZuX3V2Yl93ZWVrMikNCg0KIyBIb3cgbWFueSBhZGp1c3RlZCBwLXZhbHVlcyB3ZXJlIGxlc3MgdGhhbiAwLjA1Pw0Kc3VtKHJlc19zZm5fdXZiX3dlZWsyJHBhZGogPCAwLjEsIA0KICAgIG5hLnJtID0gVFJVRSkNCg0KIyBNQSBwbG90DQojIFNhdmUgZm9yIHB1YmxpY2F0aW9uDQp0aWZmKGZpbGVuYW1lID0gInRtcC9tYV93Ml9zZm5fdXZiLnRpZmYiLA0KICAgICBoZWlnaHQgPSA2LA0KICAgICB3aWR0aCA9IDcsDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gNjAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpwcmludChwbG90TUEocmVzX3Nmbl91dmJfd2VlazIsDQogICAgICAgICAgICAgbWFpbiA9ICJVVkIrU0ZOIHZzIFVWQiBhdCBXZWVrIDIiLA0KICAgICAgICAgICAgIGFscGhhID0gMC44KSkNCmdyYXBoaWNzLm9mZigpDQoNCnByaW50KHBsb3RNQShyZXNfc2ZuX3V2Yl93ZWVrMiwNCiAgICAgICAgICAgICBtYWluID0gIlVWQitTRk4gdnMgVVZCIGF0IFdlZWsgMiIsDQogICAgICAgICAgICAgYWxwaGEgPSAwLjgpKQ0KYGBgDQoNCiMjIEdlbmVzIHRoYXQgd2VyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhdGVkIGF0IGJvdGggY29tcGFyaXNvbnMgYXQgV2VlayAyDQpgYGB7ciBzaWduX3cyfQ0KbGdlbmUudzIuY29uIDwtIHVuaXF1ZShyZXNfY29uX3V2Yl93ZWVrMkByb3duYW1lc1tyZXNfY29uX3V2Yl93ZWVrMiRwYWRqIDwgMC4xXSkNCmxnZW5lLncyLnNmbiA8LSB1bmlxdWUocmVzX3Nmbl91dmJfd2VlazJAcm93bmFtZXNbcmVzX3Nmbl91dmJfd2VlazIkcGFkaiA8IDAuMV0pDQpsZ2VuZS53MiA8LSBsZ2VuZS53Mi5jb25bbGdlbmUudzIuY29uICVpbiUgbGdlbmUudzIuc2ZuXQ0KbGdlbmUudzIgPC0gbGdlbmUudzIgWyFpcy5uYShsZ2VuZS53MiApXQ0KbGdlbmUudzINCmBgYA0KDQpQbG90IG9mIERFU2VxLW5vcm1hbGl6ZWRjb3VudHMgb2YgZ2VuZXMgc2lnbmlmaWNhbnQgaW4gYm90aCBjb21wYXJpc29ucyBhdCBXZWVrIDI6ICAgDQoNCmBgYHtyIGRlc2VxMl93MnNpZ25fZGVzZXFub3JtfQ0KIyBHZXQgdGhlIERFU2VxLW5vcm1hbGl6ZSBjb3VudHMNCmRwMSA8LSBsaXN0KCkNCmZvciAoaSBpbiAxOmxlbmd0aChsZ2VuZS53MikpIHsNCiAgb3V0IDwtIHBsb3RDb3VudHMoZGRzLCANCiAgICAgICAgICAgICAgICAgICAgZ2VuZSA9IGxnZW5lLncyW1tpXV0sDQogICAgICAgICAgICAgICAgICAgIGludGdyb3VwID0gYygidHJ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lIiksDQogICAgICAgICAgICAgICAgICAgIHJldHVybkRhdGEgPSBUUlVFKQ0KICBkcDFbW2ldXSA8LSBkYXRhLnRhYmxlKEdlbmVpZCA9IGxnZW5lLncyW1tpXV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgU2FtcGxlID0gcm93bmFtZXMob3V0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBvdXQpDQp9DQpkcDEgPC0gcmJpbmRsaXN0KGRwMSkNCmRwMSR0cnQgPC0gZmFjdG9yKGRwMSR0cnQsDQogICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJDT04iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNGTiIpKQ0KZHAxJHRpbWUgPC0gZmFjdG9yKGRwMSR0aW1lLA0KICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIjAydyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTV3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyNXciKSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJXZWVrIDIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldlZWsgMTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldlZWsgMjUiKSkNCmRwMSRHZW5laWQgPC0gZmFjdG9yKGRwMSRHZW5laWQsDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZ2VuZS53MikNCmRwMVssIG11IDo9IG1lYW4oY291bnQsDQogICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSksDQogICAgYnkgPSBjKCJHZW5laWQiLA0KICAgICAgICAgICAidHJ0IiwNCiAgICAgICAgICAgInRpbWUiKV0NCmRtdSA8LSB1bmlxdWUoZHAxWywgLWMoIlNhbXBsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICJjb3VudCIpXSkNCmRhdGF0YWJsZShoZWFkKGRtdSksDQogICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBjbGFzcyA9ICJjZWxsLWJvcmRlciBzdHJpcGUiKSAlPiUgDQogIGZvcm1hdFJvdW5kKGNvbHVtbnMgPSA0LA0KICAgICAgICAgICAgICBkaWdpdHMgPSAyKQ0KYGBgDQoNCmBgYHtyIGRlc2VxMl93MnNpZ25fZGVzZXFub3JtX3cyX3VwX2RuLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDh9DQpkbXUudzIgPC0gZG11W3RpbWUgPT0gIldlZWsgMiIsIF0NCmRtdS53MlssIHVwLmRuIDo9IChtdVt0cnQgPT0gIlVWQiJdID4gbXVbdHJ0ID09ICJDT04iXSkgJg0KICAgICAgICAgICAgICAgKG11W3RydCA9PSAiVVZCIl0gPiBtdVt0cnQgPT0gIlNGTiJdKSwNCiAgICAgICBieSA9IEdlbmVpZF0NCnAxIDwtIGdncGxvdChkbXUudzJbdXAuZG4gPT0gVFJVRSwgXSwNCiAgICAgICAgICAgICBhZXMoeCA9IHRydCwNCiAgICAgICAgICAgICAgICAgeSA9IG11LA0KICAgICAgICAgICAgICAgICBncm91cCA9IEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgZmlsbCA9IHRydCkpICsNCiAgICAgICAgZmFjZXRfd3JhcCh+IEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJmcmVlX3kiKSArDQogICAgICAgIGdlb21fbGluZShwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkpICsNCiAgICAgICAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksDQogICAgICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICAgICAgICBzaXplID0gMywNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZSgiIikgKw0KICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoIkRpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBHZW5lcyBhdCBXZWVrIDIiKSArDQogICAgICAgIHNjYWxlX2ZpbGxfZGlzY3JldGUoIlRyZWF0bWVudCIpICsNCiAgICAgICAgZ2d0aXRsZSgiR2VuZXMgVXByZWd1bGF0ZWQgYnkgVVZCIikNCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDEpKQ0KDQp0aWZmKGZpbGVuYW1lID0gInRtcC93Ml91cF9kbi50aWZmIiwNCiAgICAgaGVpZ2h0ID0gNiwNCiAgICAgd2lkdGggPSA4LA0KICAgICB1bml0cyA9ICdpbicsDQogICAgIHJlcyA9IDMwMCwNCiAgICAgY29tcHJlc3Npb24gPSAibHp3K3AiKQ0KcHJpbnQocDEpDQpncmFwaGljcy5vZmYoKQ0KDQpwcmludChwMSkNCmBgYA0KDQojIyBEaWQgZG93bi11cC1kb3duIHRyZW5kIHBlcnNpc3Q/DQpgYGB7ciBkZXNlcTJfdzJzaWduX2Rlc2Vxbm9ybV9wbG90X2FsbF91cF9kbiwgZmlnLmhlaWdodCA9IDEwLCBmaWcud2lkdGggPSAxMn0NCmRwMS50bXAgPC0gZHAxW2RwMSRHZW5laWQgJWluJSB1bmlxdWUoZG11LncyJEdlbmVpZFtkbXUudzIkdXAuZG5dKSwgXQ0KZG11LnRtcCA8LSBkbXVbZG11JEdlbmVpZCAlaW4lIHVuaXF1ZShkbXUudzIkR2VuZWlkW2RtdS53MiR1cC5kbl0pLCBdDQpwMSA8LSBnZ3Bsb3QoZHAxLnRtcCwNCiAgICAgICAgICAgICBhZXMoeCA9IHRpbWUsDQogICAgICAgICAgICAgICAgIHkgPSBjb3VudCwNCiAgICAgICAgICAgICAgICAgZ3JvdXAgPSB0cnQsDQogICAgICAgICAgICAgICAgIGZpbGwgPSB0cnQpKSArDQogIGZhY2V0X3dyYXAofiBHZW5laWQsDQogICAgICAgICAgICAgc2NhbGUgPSAiZnJlZV95IikgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC41KSwNCiAgICAgICAgICAgICBzaGFwZSA9IDIxLA0KICAgICAgICAgICAgIHNpemUgPSA1LA0KICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGRtdS50bXAsDQogICAgICAgICAgICBhZXMoeCA9IHRpbWUsDQogICAgICAgICAgICAgICAgeSA9IG11LA0KICAgICAgICAgICAgICAgIGdyb3VwID0gdHJ0LA0KICAgICAgICAgICAgICAgIGNvbG91ciA9IHRydCksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksDQogICAgICAgICAgICBhbHBoYSA9IDAuNSwNCiAgICAgICAgICAgIHNpemUgPSAyKSArDQogIHNjYWxlX3hfZGlzY3JldGUoIiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKCJERVNlcS1Ob3JtYWxpemVkIENvdW50cyIpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZSgiVHJlYXRtZW50IikNCnByaW50KHAxKQ0KYGBgDQoNCmBgYHtyIGRlc2VxMl93MnNpZ25fZGVzZXFub3JtX3cyX2RuX3VwLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDh9DQpkbXUudzJbLCBkbi51cCA6PSAobXVbdHJ0ID09ICJVVkIiXSA8IG11W3RydCA9PSAiQ09OIl0pICYNCiAgICAgICAgICAgICAgIChtdVt0cnQgPT0gIlVWQiJdIDwgbXVbdHJ0ID09ICJTRk4iXSksDQogICAgICAgYnkgPSBHZW5laWRdDQpwMiA8LSBnZ3Bsb3QoZG11LncyW2RuLnVwID09IFRSVUUsIF0sDQogICAgICAgICAgICAgYWVzKHggPSB0cnQsDQogICAgICAgICAgICAgICAgIHkgPSBtdSwNCiAgICAgICAgICAgICAgICAgZ3JvdXAgPSBHZW5laWQsDQogICAgICAgICAgICAgICAgIGZpbGwgPSB0cnQpKSArDQogICAgICAgIGZhY2V0X3dyYXAofiBHZW5laWQsDQogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAiZnJlZV95IikgKw0KICAgICAgICBnZW9tX2xpbmUocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpKSArDQogICAgICAgIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpLA0KICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMjEsDQogICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArDQogICAgICAgIHNjYWxlX3hfZGlzY3JldGUoIiIpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKCJEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMgYXQgV2VlayAyIikgKw0KICAgICAgICBzY2FsZV9maWxsX2Rpc2NyZXRlKCJUcmVhdG1lbnQiKSArDQogICAgICAgIGdndGl0bGUoIkdlbmVzIERvd25yZWd1bGF0ZWQgYnkgVVZCIikNCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDEpKQ0KDQp0aWZmKGZpbGVuYW1lID0gInRtcC93Ml9kbl91cC50aWZmIiwNCiAgICAgaGVpZ2h0ID0gNiwNCiAgICAgd2lkdGggPSA4LA0KICAgICB1bml0cyA9ICdpbicsDQogICAgIHJlcyA9IDYwMCwNCiAgICAgY29tcHJlc3Npb24gPSAibHp3K3AiKQ0KcHJpbnQocDIpDQpncmFwaGljcy5vZmYoKQ0KDQpwcmludChwMikNCmBgYA0KDQojIyBEaWQgdXAtZG93bi11cCB0cmVuZCBwZXJzaXN0Pw0KYGBge3IgZGVzZXEyX3cyc2lnbl9kZXNlcW5vcm1fcGxvdF9hbGxfZG5fdXAsIGZpZy5oZWlnaHQgPSAxMCwgZmlnLndpZHRoID0gMTJ9DQpkcDEudG1wIDwtIGRwMVtkcDEkR2VuZWlkICVpbiUgdW5pcXVlKGRtdS53MiRHZW5laWRbZG11LncyJGRuLnVwXSksIF0NCmRtdS50bXAgPC0gZG11W2RtdSRHZW5laWQgJWluJSB1bmlxdWUoZG11LncyJEdlbmVpZFtkbXUudzIkZG4udXBdKSwgXQ0KcDEgPC0gZ2dwbG90KGRwMS50bXAsDQogICAgICAgICAgICAgYWVzKHggPSB0aW1lLA0KICAgICAgICAgICAgICAgICB5ID0gY291bnQsDQogICAgICAgICAgICAgICAgIGdyb3VwID0gdHJ0LA0KICAgICAgICAgICAgICAgICBmaWxsID0gdHJ0KSkgKw0KICBmYWNldF93cmFwKH4gR2VuZWlkLA0KICAgICAgICAgICAgIHNjYWxlID0gImZyZWVfeSIpICsNCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksDQogICAgICAgICAgICAgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICBzaXplID0gNSwNCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBkbXUudG1wLA0KICAgICAgICAgICAgYWVzKHggPSB0aW1lLA0KICAgICAgICAgICAgICAgIHkgPSBtdSwNCiAgICAgICAgICAgICAgICBncm91cCA9IHRydCwNCiAgICAgICAgICAgICAgICBjb2xvdXIgPSB0cnQpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpLA0KICAgICAgICAgICAgYWxwaGEgPSAwLjUsDQogICAgICAgICAgICBzaXplID0gMikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKCIiKSArDQogIHNjYWxlX3lfY29udGludW91cygiREVTZXEtTm9ybWFsaXplZCBDb3VudHMiKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUoIlRyZWF0bWVudCIpDQpwcmludChwMSkNCmBgYA0KDQpJbiBtYW55IG9mIHRoZXNlIGdlbmVzLCBVVkIrU0ZOIG1vdmVkIGNsb3NlciB0byBVVkIgb3ZlciB0aW1lLg0KDQojIyBIZWF0bWFwIGZvciBXZWVrIDIgZGlmZmVyZW50aWFsbHkgbWV0aHlsYXRlZCBnZW5lcw0KYGBge3IgdzJfaGVhdG1hcCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OH0NCnVwLmRuLncyIDwtIHVuaXF1ZShhcy5jaGFyYWN0ZXIoZG11LncyJEdlbmVpZFtkbXUudzIkdXAuZG5dKSkNCmRuLnVwLncyIDwtIHVuaXF1ZShhcy5jaGFyYWN0ZXIoZG11LncyJEdlbmVpZFtkbXUudzIkZG4udXBdKSkNCmxsIDwtIHVuaXF1ZShjKHVwLmRuLncyLA0KICAgICAgICAgICAgICAgZG4udXAudzIpKQ0KIyAzNiBnZW5lcw0KDQpjb25fdXZiX3dlZWsyIDwtIGRhdGEudGFibGUoR2VuZWlkID0gcmVzX2Nvbl91dmJfd2VlazJAcm93bmFtZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nMkZvbGRDaGFuZ2UgPSByZXNfY29uX3V2Yl93ZWVrMkBsaXN0RGF0YSRsb2cyRm9sZENoYW5nZSkNCg0Kc2ZuX3V2Yl93ZWVrMiA8LSBkYXRhLnRhYmxlKEdlbmVpZCA9IHJlc19zZm5fdXZiX3dlZWsyQHJvd25hbWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZzJGb2xkQ2hhbmdlID0gLXJlc19zZm5fdXZiX3dlZWsyQGxpc3REYXRhJGxvZzJGb2xkQ2hhbmdlKQ0KDQp0MSA8LSBtZXJnZShjb25fdXZiX3dlZWsyW2Nvbl91dmJfd2VlazIkR2VuZWlkICVpbiUgbGwsIF0sDQogICAgICAgICAgICBzZm5fdXZiX3dlZWsyW3Nmbl91dmJfd2VlazIkR2VuZWlkICVpbiUgbGwsIF0sDQogICAgICAgICAgICBieSA9ICJHZW5laWQiKQ0KY29sbmFtZXModDEpWzI6M10gPC0gYygiQ29udHJvbCB2cy4gVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIlVWQiB2cy4gU0ZOK1VWQiIpDQp0MSA8LSB0MVtvcmRlcih0MSRgQ29udHJvbCB2cy4gVVZCYCwNCiAgICAgICAgICAgICAgIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQ0Kd3JpdGUuY3N2KHQxLA0KICAgICAgICAgIGZpbGUgPSAidG1wL3cyX3NpZ25fY2hhbmdlcy5jc3YiLA0KICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQ0KDQpsbCA8LSBtZWx0LmRhdGEudGFibGUoZGF0YSA9IHQxLA0KICAgICAgICAgICAgICAgICAgICAgIGlkLnZhcnMgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgIG1lYXN1cmUudmFycyA9IDI6MywNCiAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5uYW1lID0gIkNvbXBhcmlzb24iLA0KICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLm5hbWUgPSAiR2VuZSBFeHByZXNzaW9uIERpZmYiKQ0KbGwkQ29tcGFyaXNvbiA8LSBmYWN0b3IobGwkQ29tcGFyaXNvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkNvbnRyb2wgdnMuIFVWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVVkIgdnMuIFNGTitVVkIiKSkNCmx2bHMgPC0gbGxbbGwkQ29tcGFyaXNvbiA9PSAiQ29udHJvbCB2cy4gVVZCIiwgXQ0KbGwkR2VuZWlkIDwtIGZhY3RvcihsbCRHZW5laWQsDQogICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGx2bHMkR2VuZWlkW29yZGVyKGx2bHMkYEdlbmUgRXhwcmVzc2lvbiBEaWZmYCldKQ0KDQojIEFkZCBkZW5kcm9ncmFtLS0tLQ0KZHQuZG5kciA8LSBkYXRhLmZyYW1lKHQxW0dlbmVpZCAlaW4lIGxldmVscyhsbCRHZW5laWQpLCBdKQ0Kcm93bmFtZXMoZHQuZG5kcikgPC0gZHQuZG5kciRHZW5lDQpkdC5kbmRyIDwtIGR0LmRuZHJbLCAtMV0NCg0KIyBDb21wdXRlIGRpc3RhbmNlcyBiZXR3ZWVuIGdlbmVzLS0tLQ0Kc2FtcGxlRGlzdHMgPC0gZGlzdChkdC5kbmRyKQ0KDQojIE1ha2UgZGVuZHJvZ3JhbSBkYXRhLS0tLQ0KZGhjIDwtIGFzLmRlbmRyb2dyYW0oaGNsdXN0KGQgPSBzYW1wbGVEaXN0cyksDQogICAgICAgICAgICAgICAgICAgICBob3JpeiA9IFRSVUUpDQpkZGF0YSA8LSBkZW5kcm9fZGF0YShkaGMsIA0KICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZWN0YW5nbGUiKQ0KDQojIFNlZ21lbnQgZGF0YS0tLS0NCmR0cDEgPC0gc2VnbWVudChkZGF0YSkNCg0KIyBIaXRtYXAgZGF0YS0tLS0NCmR0cDIgPC0gbGwNCmR0cDIkR2VuZWlkIDwtIGZhY3RvcihkdHAyJEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBkZGF0YSRsYWJlbHMkbGFiZWwpDQoNCm9mZnNldC5zaXplIDwtIDQNCg0KcDEgPC0gZ2dwbG90KGRhdGEgPSBkdHAyKSArDQogIGNvb3JkX3BvbGFyKCJ5IiwNCiAgICAgICAgICAgICAgc3RhcnQgPSAtMC4zLA0KICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAtMSkgKw0KICBnZW9tX3RpbGUoYWVzKHggPSAgYXMubnVtZXJpYyhDb21wYXJpc29uKSwNCiAgICAgICAgICAgICAgICB5ID0gR2VuZWlkLCANCiAgICAgICAgICAgICAgICBmaWxsID0gYEdlbmUgRXhwcmVzc2lvbiBEaWZmYCksDQogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBkdHAyW0NvbXBhcmlzb24gPT0gIkNvbnRyb2wgdnMuIFVWQiIsIF0sDQogICAgICAgICAgICBhZXMoeCA9IHJlcCgxLjc1LA0KICAgICAgICAgICAgICAgICAgICAgICAgbmxldmVscyhHZW5laWQpKSwNCiAgICAgICAgICAgICAgICB5ID0gR2VuZWlkLA0KICAgICAgICAgICAgICAgIGFuZ2xlID0gOTAgKyBzZXEoZnJvbSA9IDMwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSAzMzAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gbmxldmVscyhHZW5laWQpKVthcy5udW1lcmljKEdlbmVpZCldICsgDQogICAgICAgICAgICAgICAgICBvZmZzZXQuc2l6ZSwNCiAgICAgICAgICAgICAgICBsYWJlbCA9IHVuaXF1ZShHZW5laWQpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMCkgKw0KICBnZW9tX3RleHQoZGF0YSA9IGR0cDJbR2VuZWlkID09IGxldmVscyhkdHAyJEdlbmVpZClbMV0sIF0sDQogICAgICAgICAgICBhZXMoeCA9IDE6bmxldmVscyhDb21wYXJpc29uKSwNCiAgICAgICAgICAgICAgICB5ID0gcmVwKC1vZmZzZXQuc2l6ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5sZXZlbHMoQ29tcGFyaXNvbikpLA0KICAgICAgICAgICAgICAgIGFuZ2xlID0gMCwNCiAgICAgICAgICAgICAgICBsYWJlbCA9IGxldmVscyhDb21wYXJpc29uKSksDQogICAgICAgICAgICBoanVzdCA9IDEsDQogICAgICAgICAgICBzaXplID0gNSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAicmVkIiwgDQogICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiZ3JlZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gImdyZXkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArDQogIHNjYWxlX3lfZGlzY3JldGUoIiIsDQogICAgICAgICAgICAgICAgICAgZXhwYW5kID0gYygwLCAwKSkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLA0KICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLA0KICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgxLCAiaW4iKSwNCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuMywgImluIikpICsNCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBkdHAxLA0KICAgICAgICAgICAgICAgYWVzKHggPSAtc3FydCh5KSArIDAuNSwNCiAgICAgICAgICAgICAgICAgICB5ID0geCwgDQogICAgICAgICAgICAgICAgICAgeGVuZCA9IC1zcXJ0KHllbmQpICsgMC41LA0KICAgICAgICAgICAgICAgICAgIHllbmQgPSB4ZW5kKSwNCiAgICAgICAgICAgICAgIHNpemUgPSAxKSANCg0KdGlmZihmaWxlbmFtZSA9ICJ0bXAvc2tpbl91YnZfdzJfc2ZuX2hpdG1hcF93aXRoX3BoeWxvLnRpZmYiLA0KICAgICBoZWlnaHQgPSA4LA0KICAgICB3aWR0aCA9IDgsDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gNjAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpwbG90KHAxKQ0KZ3JhcGhpY3Mub2ZmKCkNCg0KcHJpbnQocDEpDQpgYGANCg0KIyMgVmVubiBEaWFncmFtLCBXZWVrIDINCmBgYHtyIHcyLXZlbm4sIGZpZy5oZWlnaHQ9NixmaWcud2lkdGg9NH0NCiMgMS4gQ3RybCB2cy4gVVZCDQojIGFkanVzdGVkIHAtdmFsdWUgPCAwLjENCiMgTEZDID4gMCAodXApICAgICAgIDogMTU0NiwgOSUNCiMgTEZDIDwgMCAoZG93bikgICAgIDogMTUzNywgOC45JQ0KIyAyMyBnZW5lcyBkb3duLXVwLWRvd24NCg0KIyAyLiBTRk4rVVZCIHZzLiBVVkINCiMgYWRqdXN0ZWQgcC12YWx1ZSA8IDAuMQ0KIyBMRkMgPiAwICh1cCkgICAgICAgOiAyNiwgMC4xNSUNCiMgTEZDIDwgMCAoZG93bikgICAgIDogMzUsIDAuMiUNCiMgMTMgZ2VucyB1cC1kb3duLXVwDQoNCnAxIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9jaXJjbGUoYWVzKHgwID0gYygxLCAyLCAxLCAyKSwNCiAgICAgICAgICAgICAgICAgIHkwID0gYygxLCAxLCA0LCA0KSwNCiAgICAgICAgICAgICAgICAgIHIgPSByZXAoMSwgNCksDQogICAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcihjKDIsIDEsIDEsIDIpKSksDQogICAgICAgICAgICAgIHNpemUgPSAyKSArDQogIGdlb21fdGV4dChhZXMoeCA9IHJlcChjKDAuNSwgMS41LCAyLjUpLCAyKSwNCiAgICAgICAgICAgICAgICB5ID0gcmVwKGMoMSwgNCksIGVhY2ggPSAzKSwNCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChjKDI2LCAxMywgMzUsIDE1NDYsIDIzLCAxNTM3KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiaWcubWFyayA9ICIsIikpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJncmVlbiIsICJyZWQiKSkgKw0KICB0aGVtZV92b2lkKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCnRpZmYoZmlsZW5hbWUgPSAidG1wL3NraW5fdWJ2X3Nmbl93Ml92ZW5uLnRpZmYiLA0KICAgICBoZWlnaHQgPSA2LA0KICAgICB3aWR0aCA9IDQsDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gNjAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpwbG90KHAxKQ0KZ3JhcGhpY3Mub2ZmKCkNCg0KcHJpbnQocDEpDQpgYGANCg0KIyMgRWZmZWN0IG9mIFVWQiBhdCBXZWVrIDE1DQpgYGB7ciBkZXNlcTJfcmVzdWx0c193ZWVrMTVfY29uX3V2Yn0NCnJlc19jb25fdXZiX3dlZWsxNSA8LSByZXN1bHRzKGRkcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJhc3QgPSBjKDAsMCwwLDEsMCwxLDAsMCwwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjEpDQojIE5PVCBUSEUgU0FNRSBBUz8hISE6DQojIHJlc19jb25fdXZiX3dlZWsxNSA8LSByZXN1bHRzKGRkcywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cmFzdCA9IGxpc3QoInRydF9DT05fdnNfVVZCIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUxNXcudHJ0Q09OIiksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjEpDQoNCiMgb3V0IG9mIDE3MjAyIHdpdGggbm9uemVybyB0b3RhbCByZWFkIGNvdW50DQojIGFkanVzdGVkIHAtdmFsdWUgPCAwLjENCiMgTEZDID4gMCAodXApICAgICAgIDogMTUxMywgOC44JQ0KIyBMRkMgPCAwIChkb3duKSAgICAgOiAxNDYzLCA4LjUlDQojIG91dGxpZXJzIFsxXSAgICAgICA6IDAsIDAlDQojIGxvdyBjb3VudHMgWzJdICAgICA6IDI2NjgsIDE2JQ0KIyAobWVhbiBjb3VudCA8IDIpDQojIFsxXSBzZWUgJ2Nvb2tzQ3V0b2ZmJyBhcmd1bWVudCBvZiA/cmVzdWx0cw0KIyBbMl0gc2VlICdpbmRlcGVuZGVudEZpbHRlcmluZycgYXJndW1lbnQgb2YgP3Jlc3VsdHMNCiMgDQojIFsxXSAyOTc2DQojIA0KIyBvdXQgb2YgMTcyMDIgd2l0aCBub256ZXJvIHRvdGFsIHJlYWQgY291bnQNCiMgYWRqdXN0ZWQgcC12YWx1ZSA8IDAuMQ0KIyBMRkMgPiAwICh1cCkgICAgICAgOiA0NjksIDIuNyUNCiMgTEZDIDwgMCAoZG93bikgICAgIDogNDU1LCAyLjYlDQojIG91dGxpZXJzIFsxXSAgICAgICA6IDAsIDAlDQojIGxvdyBjb3VudHMgWzJdICAgICA6IDQwMDIsIDIzJQ0KIyAobWVhbiBjb3VudCA8IDYpDQojIFsxXSBzZWUgJ2Nvb2tzQ3V0b2ZmJyBhcmd1bWVudCBvZiA/cmVzdWx0cw0KIyBbMl0gc2VlICdpbmRlcGVuZGVudEZpbHRlcmluZycgYXJndW1lbnQgb2YgP3Jlc3VsdHMNCiMgDQojIFsxXSA5MjQNCg0KcmVzX2Nvbl91dmJfd2VlazE1IDwtIHJlc19jb25fdXZiX3dlZWsxNVtvcmRlcihyZXNfY29uX3V2Yl93ZWVrMTUkcGFkaiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY3JlYXNpbmcgPSBGQUxTRSksXQ0Kc3VtbWFyeShyZXNfY29uX3V2Yl93ZWVrMTUpDQoNCiMgSG93IG1hbnkgYWRqdXN0ZWQgcC12YWx1ZXMgd2VyZSBsZXNzIHRoYW4gMC4wMT8NCnN1bShyZXNfY29uX3V2Yl93ZWVrMTUkcGFkaiA8IDAuMSwgDQogICAgbmEucm0gPSBUUlVFKQ0KDQojIE1BIHBsb3QNCiMgU2F2ZSBmb3IgcHVibGljYXRpb24NCnRpZmYoZmlsZW5hbWUgPSAidG1wL21hX3cxNV9jb25fdXZiLnRpZmYiLA0KICAgICBoZWlnaHQgPSA2LA0KICAgICB3aWR0aCA9IDcsDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gNjAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpwbG90TUEocmVzX2Nvbl91dmJfd2VlazE1LA0KICAgICAgICAgICAgIG1haW4gPSAiQ29udHJvbCB2cy4gVVZCIGF0IFdlZWsgMTUiLA0KICAgICAgICAgICAgIGFscGhhID0gMC44KQ0KZ3JhcGhpY3Mub2ZmKCkNCg0KcGxvdE1BKHJlc19jb25fdXZiX3dlZWsxNSwNCiAgICAgICAgICAgICBtYWluID0gIkNvbnRyb2wgdnMuIFVWQiBhdCBXZWVrIDE1IiwNCiAgICAgICAgICAgICBhbHBoYSA9IDAuOCkNCmBgYA0KIyMgUHJvdGVjdGl2ZSBlZmZlY3Qgb2YgU0ZOIGF0IFdlZWsgMTUNCmBgYHtyIGRlc2VxMl9yZXN1bHRzX3dlZWsxNV9zZm5fdXZifQ0KcmVzX3Nmbl91dmJfd2VlazE1IDwtIHJlc3VsdHMoZGRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cmFzdCA9IGMoMCwwLDAsMCwxLDAsMCwxLDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMSkNCiMgTk9UIFRIRSBTQU1FIEFTISEhOg0KIyByZXNfc2ZuX3V2Yl93ZWVrMTUgPC0gcmVzdWx0cyhkZHMsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJhc3QgPSBsaXN0KCJ0cnRfU0ZOX3ZzX1VWQiIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lMTV3LnRydFNGTiIpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC4xKQ0KcmVzX3Nmbl91dmJfd2VlazE1IDwtIHJlc19zZm5fdXZiX3dlZWsxNVtvcmRlcihyZXNfc2ZuX3V2Yl93ZWVrMTUkcGFkaiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY3JlYXNpbmcgPSBGQUxTRSksXQ0Kc3VtbWFyeShyZXNfc2ZuX3V2Yl93ZWVrMTUpDQoNCiMgSG93IG1hbnkgYWRqdXN0ZWQgcC12YWx1ZXMgd2VyZSBsZXNzIHRoYW4gMC4wNT8NCnN1bShyZXNfc2ZuX3V2Yl93ZWVrMTUkcGFkaiA8IDAuMSwgDQogICAgbmEucm0gPSBUUlVFKQ0KDQojIE1BIHBsb3QNCiMgU2F2ZSBmb3IgcHVibGljYXRpb24NCnRpZmYoZmlsZW5hbWUgPSAidG1wL21hX3cyX3Nmbl91dmIudGlmZiIsDQogICAgIGhlaWdodCA9IDYsDQogICAgIHdpZHRoID0gNywNCiAgICAgdW5pdHMgPSAnaW4nLA0KICAgICByZXMgPSA2MDAsDQogICAgIGNvbXByZXNzaW9uID0gImx6dytwIikNCnByaW50KHBsb3RNQShyZXNfc2ZuX3V2Yl93ZWVrMTUsDQogICAgICAgICAgICAgbWFpbiA9ICJVVkIrU0ZOIHZzIFVWQiBhdCBXZWVrIDE1IiwNCiAgICAgICAgICAgICBhbHBoYSA9IDAuOCkpDQpncmFwaGljcy5vZmYoKQ0KDQpwcmludChwbG90TUEocmVzX3Nmbl91dmJfd2VlazE1LA0KICAgICAgICAgICAgIG1haW4gPSAiVVZCK1NGTiB2cyBVVkIgYXQgV2VlayAxNSIsDQogICAgICAgICAgICAgYWxwaGEgPSAwLjgpKQ0KYGBgDQoNCiMjIEdlbmVzIHRoYXQgd2VyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhdGVkIGF0IGJvdGggY29tcGFyaXNvbnMgYXQgV2VlayAxNQ0KYGBge3Igc2lnbl93MTV9DQpsZ2VuZS53MTUuY29uIDwtIHVuaXF1ZShyZXNfY29uX3V2Yl93ZWVrMTVAcm93bmFtZXNbcmVzX2Nvbl91dmJfd2VlazE1JHBhZGogPCAwLjFdKQ0KbGdlbmUudzE1LnNmbiA8LSB1bmlxdWUocmVzX3Nmbl91dmJfd2VlazE1QHJvd25hbWVzW3Jlc19zZm5fdXZiX3dlZWsxNSRwYWRqIDwgMC4xXSkNCmxnZW5lLncxNSA8LSBsZ2VuZS53MTUuY29uW2xnZW5lLncxNS5jb24gJWluJSBsZ2VuZS53MTUuc2ZuXQ0KbGdlbmUudzE1IDwtIGxnZW5lLncxNSBbIWlzLm5hKGxnZW5lLncxNSApXQ0KbGVuZ3RoKHVuaXF1ZShsZ2VuZS53MTUpKQ0KYGBgDQpQbG90IG9mIERFU2VxLW5vcm1hbGl6ZWRjb3VudHMgb2YgZ2VuZXMgc2lnbmlmaWNhbnQgaW4gYm90aCBjb21wYXJpc29ucyBhdCBXZWVrIDE1OiAgIA0KDQpgYGB7ciBkZXNlcTJfdzE1c2lnbl9kZXNlcW5vcm19DQojIEdldCB0aGUgREVTZXEtbm9ybWFsaXplIGNvdW50cw0KZHAxIDwtIGxpc3QoKQ0KZm9yIChpIGluIDE6bGVuZ3RoKGxnZW5lLncxNSkpIHsNCiAgb3V0IDwtIHBsb3RDb3VudHMoZGRzLCANCiAgICAgICAgICAgICAgICAgICAgZ2VuZSA9IGxnZW5lLncxNVtbaV1dLA0KICAgICAgICAgICAgICAgICAgICBpbnRncm91cCA9IGMoInRydCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidGltZSIpLA0KICAgICAgICAgICAgICAgICAgICByZXR1cm5EYXRhID0gVFJVRSkNCiAgZHAxW1tpXV0gPC0gZGF0YS50YWJsZShHZW5laWQgPSBsZ2VuZS53MTVbW2ldXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGUgPSByb3duYW1lcyhvdXQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG91dCkNCn0NCmRwMSA8LSByYmluZGxpc3QoZHAxKQ0KZHAxJHRydCA8LSBmYWN0b3IoZHAxJHRydCwNCiAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkNPTiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVVkIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0ZOIikpDQpkcDEkdGltZSA8LSBmYWN0b3IoZHAxJHRpbWUsDQogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiMDJ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxNXciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjI1dyIpLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIldlZWsgMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV2VlayAxNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV2VlayAyNSIpKQ0KZHAxJEdlbmVpZCA8LSBmYWN0b3IoZHAxJEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxnZW5lLncxNSkNCmRwMVssIG11IDo9IG1lYW4oY291bnQsDQogICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSksDQogICAgYnkgPSBjKCJHZW5laWQiLA0KICAgICAgICAgICAidHJ0IiwNCiAgICAgICAgICAgInRpbWUiKV0NCmRtdSA8LSB1bmlxdWUoZHAxWywgLWMoIlNhbXBsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICJjb3VudCIpXSkNCmRhdGF0YWJsZShoZWFkKGRtdSksDQogICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBjbGFzcyA9ICJjZWxsLWJvcmRlciBzdHJpcGUiKSAlPiUgDQogIGZvcm1hdFJvdW5kKGNvbHVtbnMgPSA0LA0KICAgICAgICAgICAgICBkaWdpdHMgPSAyKQ0KYGBgDQoNCmBgYHtyIGRlc2VxMl93MTVzaWduX2Rlc2Vxbm9ybV93MTVfdXBfZG4sIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOH0NCmRtdS53MTUgPC0gZG11W3RpbWUgPT0gIldlZWsgMTUiLCBdDQpkbXUudzE1WywgdXAuZG4gOj0gKG11W3RydCA9PSAiVVZCIl0gPiBtdVt0cnQgPT0gIkNPTiJdKSAmDQogICAgICAgICAgICAgICAobXVbdHJ0ID09ICJVVkIiXSA+IG11W3RydCA9PSAiU0ZOIl0pLA0KICAgICAgIGJ5ID0gR2VuZWlkXQ0KcDEgPC0gZ2dwbG90KGRtdS53MTVbdXAuZG4gPT0gVFJVRSwgXSwNCiAgICAgICAgICAgICBhZXMoeCA9IHRydCwNCiAgICAgICAgICAgICAgICAgeSA9IG11LA0KICAgICAgICAgICAgICAgICBncm91cCA9IEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgZmlsbCA9IHRydCkpICsNCiAgICAgICAgZmFjZXRfd3JhcCh+IEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJmcmVlX3kiKSArDQogICAgICAgIGdlb21fbGluZShwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkpICsNCiAgICAgICAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksDQogICAgICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICAgICAgICBzaXplID0gMywNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZSgiIikgKw0KICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoIkRpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBHZW5lcyBhdCBXZWVrIDE1IikgKw0KICAgICAgICBzY2FsZV9maWxsX2Rpc2NyZXRlKCJUcmVhdG1lbnQiKSArDQogICAgICAgIGdndGl0bGUoIkdlbmVzIFVwcmVndWxhdGVkIGJ5IFVWQiIpDQp0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAxKSkNCg0KdGlmZihmaWxlbmFtZSA9ICJ0bXAvdzE1X3VwX2RuLnRpZmYiLA0KICAgICBoZWlnaHQgPSA2LA0KICAgICB3aWR0aCA9IDgsDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gMzAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpwcmludChwMSkNCmdyYXBoaWNzLm9mZigpDQoNCnByaW50KHAxKQ0KYGBgDQoNCiMjIERpZCBkb3duLXVwLWRvd24gdHJlbmQgcGVyc2lzdD8NCmBgYHtyIGRlc2VxMl93MTVzaWduX2Rlc2Vxbm9ybV9wbG90X2FsbF91cF9kbiwgZmlnLmhlaWdodCA9IDEwLCBmaWcud2lkdGggPSAxMn0NCmRwMS50bXAgPC0gZHAxW2RwMSRHZW5laWQgJWluJSB1bmlxdWUoZG11LncxNSRHZW5laWRbZG11LncxNSR1cC5kbl0pLCBdDQpkbXUudG1wIDwtIGRtdVtkbXUkR2VuZWlkICVpbiUgdW5pcXVlKGRtdS53MTUkR2VuZWlkW2RtdS53MTUkdXAuZG5dKSwgXQ0KcDEgPC0gZ2dwbG90KGRwMS50bXAsDQogICAgICAgICAgICAgYWVzKHggPSB0aW1lLA0KICAgICAgICAgICAgICAgICB5ID0gY291bnQsDQogICAgICAgICAgICAgICAgIGdyb3VwID0gdHJ0LA0KICAgICAgICAgICAgICAgICBmaWxsID0gdHJ0KSkgKw0KICBmYWNldF93cmFwKH4gR2VuZWlkLA0KICAgICAgICAgICAgIHNjYWxlID0gImZyZWVfeSIpICsNCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksDQogICAgICAgICAgICAgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICBzaXplID0gNSwNCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBkbXUudG1wLA0KICAgICAgICAgICAgYWVzKHggPSB0aW1lLA0KICAgICAgICAgICAgICAgIHkgPSBtdSwNCiAgICAgICAgICAgICAgICBncm91cCA9IHRydCwNCiAgICAgICAgICAgICAgICBjb2xvdXIgPSB0cnQpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpLA0KICAgICAgICAgICAgYWxwaGEgPSAwLjUsDQogICAgICAgICAgICBzaXplID0gMikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKCIiKSArDQogIHNjYWxlX3lfY29udGludW91cygiREVTZXEtTm9ybWFsaXplZCBDb3VudHMiKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUoIlRyZWF0bWVudCIpDQpwcmludChwMSkNCmBgYA0KDQpgYGB7ciBkZXNlcTJfdzE1c2lnbl9kZXNlcW5vcm1fdzE1X2RuX3VwLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDh9DQpkbXUudzE1WywgZG4udXAgOj0gKG11W3RydCA9PSAiVVZCIl0gPCBtdVt0cnQgPT0gIkNPTiJdKSAmDQogICAgICAgICAgICAgICAobXVbdHJ0ID09ICJVVkIiXSA8IG11W3RydCA9PSAiU0ZOIl0pLA0KICAgICAgIGJ5ID0gR2VuZWlkXQ0KcDIgPC0gZ2dwbG90KGRtdS53MTVbZG4udXAgPT0gVFJVRSwgXSwNCiAgICAgICAgICAgICBhZXMoeCA9IHRydCwNCiAgICAgICAgICAgICAgICAgeSA9IG11LA0KICAgICAgICAgICAgICAgICBncm91cCA9IEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgZmlsbCA9IHRydCkpICsNCiAgICAgICAgZmFjZXRfd3JhcCh+IEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJmcmVlX3kiKSArDQogICAgICAgIGdlb21fbGluZShwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkpICsNCiAgICAgICAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksDQogICAgICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICAgICAgICBzaXplID0gMywNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZSgiIikgKw0KICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoIkRpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBHZW5lcyBhdCBXZWVrIDE1IikgKw0KICAgICAgICBzY2FsZV9maWxsX2Rpc2NyZXRlKCJUcmVhdG1lbnQiKSArDQogICAgICAgIGdndGl0bGUoIkdlbmVzIERvd25yZWd1bGF0ZWQgYnkgVVZCIikNCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDEpKQ0KDQp0aWZmKGZpbGVuYW1lID0gInRtcC93MTVfZG5fdXAudGlmZiIsDQogICAgIGhlaWdodCA9IDYsDQogICAgIHdpZHRoID0gOCwNCiAgICAgdW5pdHMgPSAnaW4nLA0KICAgICByZXMgPSA2MDAsDQogICAgIGNvbXByZXNzaW9uID0gImx6dytwIikNCnByaW50KHAyKQ0KZ3JhcGhpY3Mub2ZmKCkNCg0KcHJpbnQocDIpDQpgYGANCg0KIyMgRGlkIHVwLWRvd24tdXAgdHJlbmQgcGVyc2lzdD8NCmBgYHtyIGRlc2VxMl93MTVzaWduX2Rlc2Vxbm9ybV9wbG90X2FsbF9kbl91cCwgZmlnLmhlaWdodCA9IDEwLCBmaWcud2lkdGggPSAxMn0NCmRwMS50bXAgPC0gZHAxW2RwMSRHZW5laWQgJWluJSB1bmlxdWUoZG11LncxNSRHZW5laWRbZG11LncxNSRkbi51cF0pLCBdDQpkbXUudG1wIDwtIGRtdVtkbXUkR2VuZWlkICVpbiUgdW5pcXVlKGRtdS53MTUkR2VuZWlkW2RtdS53MTUkZG4udXBdKSwgXQ0KcDEgPC0gZ2dwbG90KGRwMS50bXAsDQogICAgICAgICAgICAgYWVzKHggPSB0aW1lLA0KICAgICAgICAgICAgICAgICB5ID0gY291bnQsDQogICAgICAgICAgICAgICAgIGdyb3VwID0gdHJ0LA0KICAgICAgICAgICAgICAgICBmaWxsID0gdHJ0KSkgKw0KICBmYWNldF93cmFwKH4gR2VuZWlkLA0KICAgICAgICAgICAgIHNjYWxlID0gImZyZWVfeSIpICsNCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksDQogICAgICAgICAgICAgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICBzaXplID0gNSwNCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBkbXUudG1wLA0KICAgICAgICAgICAgYWVzKHggPSB0aW1lLA0KICAgICAgICAgICAgICAgIHkgPSBtdSwNCiAgICAgICAgICAgICAgICBncm91cCA9IHRydCwNCiAgICAgICAgICAgICAgICBjb2xvdXIgPSB0cnQpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpLA0KICAgICAgICAgICAgYWxwaGEgPSAwLjUsDQogICAgICAgICAgICBzaXplID0gMikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKCIiKSArDQogIHNjYWxlX3lfY29udGludW91cygiREVTZXEtTm9ybWFsaXplZCBDb3VudHMiKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUoIlRyZWF0bWVudCIpDQpwcmludChwMSkNCmBgYA0KDQojIyBIZWF0bWFwIGZvciBXZWVrIDE1IGRpZmZlcmVudGlhbGx5IG1ldGh5bGF0ZWQgZ2VuZXMNCmBgYHtyIHcxNV9oZWF0bWFwLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQ0KdXAuZG4udzE1IDwtIHVuaXF1ZShhcy5jaGFyYWN0ZXIoZG11LncxNSRHZW5laWRbZG11LncxNSR1cC5kbl0pKQ0KZG4udXAudzE1IDwtIHVuaXF1ZShhcy5jaGFyYWN0ZXIoZG11LncxNSRHZW5laWRbZG11LncxNSRkbi51cF0pKQ0KbGwgPC0gdW5pcXVlKGModXAuZG4udzE1LA0KICAgICAgICAgICAgICAgZG4udXAudzE1KSkNCiMgMTYgZ2VuZXMNCg0KY29uX3V2Yl93ZWVrMTUgPC0gZGF0YS50YWJsZShHZW5laWQgPSByZXNfY29uX3V2Yl93ZWVrMTVAcm93bmFtZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZzJGb2xkQ2hhbmdlID0gcmVzX2Nvbl91dmJfd2VlazE1QGxpc3REYXRhJGxvZzJGb2xkQ2hhbmdlKQ0KDQpzZm5fdXZiX3dlZWsxNSA8LSBkYXRhLnRhYmxlKEdlbmVpZCA9IHJlc19zZm5fdXZiX3dlZWsxNUByb3duYW1lcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nMkZvbGRDaGFuZ2UgPSAtcmVzX3Nmbl91dmJfd2VlazE1QGxpc3REYXRhJGxvZzJGb2xkQ2hhbmdlKQ0KDQp0MSA8LSBtZXJnZShjb25fdXZiX3dlZWsxNVtjb25fdXZiX3dlZWsxNSRHZW5laWQgJWluJSBsbCwgXSwNCiAgICAgICAgICAgIHNmbl91dmJfd2VlazE1W3Nmbl91dmJfd2VlazE1JEdlbmVpZCAlaW4lIGxsLCBdLA0KICAgICAgICAgICAgYnkgPSAiR2VuZWlkIikNCmNvbG5hbWVzKHQxKVsyOjNdIDwtIGMoIkNvbnRyb2wgdnMuIFVWQiIsDQogICAgICAgICAgICAgICAgICAgICAgICJVVkIgdnMuIFNGTitVVkIiKQ0KdDEgPC0gdDFbb3JkZXIodDEkYENvbnRyb2wgdnMuIFVWQmAsDQogICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gVFJVRSksIF0NCndyaXRlLmNzdih0MSwNCiAgICAgICAgICBmaWxlID0gInRtcC93MTVfc2lnbl9jaGFuZ2VzLmNzdiIsDQogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpDQoNCmxsIDwtIG1lbHQuZGF0YS50YWJsZShkYXRhID0gdDEsDQogICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZS52YXJzID0gMjozLA0KICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiQ29tcGFyaXNvbiIsDQogICAgICAgICAgICAgICAgICAgICAgdmFsdWUubmFtZSA9ICJHZW5lIEV4cHJlc3Npb24gRGlmZiIpDQpsbCRDb21wYXJpc29uIDwtIGZhY3RvcihsbCRDb21wYXJpc29uLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiQ29udHJvbCB2cy4gVVZCIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVWQiB2cy4gU0ZOK1VWQiIpKQ0KbHZscyA8LSBsbFtsbCRDb21wYXJpc29uID09ICJDb250cm9sIHZzLiBVVkIiLCBdDQpsbCRHZW5laWQgPC0gZmFjdG9yKGxsJEdlbmVpZCwNCiAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbHZscyRHZW5laWRbb3JkZXIobHZscyRgR2VuZSBFeHByZXNzaW9uIERpZmZgKV0pDQoNCiMgQWRkIGRlbmRyb2dyYW0tLS0tDQpkdC5kbmRyIDwtIGRhdGEuZnJhbWUodDFbR2VuZWlkICVpbiUgbGV2ZWxzKGxsJEdlbmVpZCksIF0pDQpyb3duYW1lcyhkdC5kbmRyKSA8LSBkdC5kbmRyJEdlbmUNCmR0LmRuZHIgPC0gZHQuZG5kclssIC0xXQ0KDQojIENvbXB1dGUgZGlzdGFuY2VzIGJldHdlZW4gZ2VuZXMtLS0tDQpzYW1wbGVEaXN0cyA8LSBkaXN0KGR0LmRuZHIpDQoNCiMgTWFrZSBkZW5kcm9ncmFtIGRhdGEtLS0tDQpkaGMgPC0gYXMuZGVuZHJvZ3JhbShoY2x1c3QoZCA9IHNhbXBsZURpc3RzKSwNCiAgICAgICAgICAgICAgICAgICAgIGhvcml6ID0gVFJVRSkNCmRkYXRhIDwtIGRlbmRyb19kYXRhKGRoYywgDQogICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlY3RhbmdsZSIpDQoNCiMgU2VnbWVudCBkYXRhLS0tLQ0KZHRwMSA8LSBzZWdtZW50KGRkYXRhKQ0KDQojIEhpdG1hcCBkYXRhLS0tLQ0KZHRwMiA8LSBsbA0KZHRwMiRHZW5laWQgPC0gZmFjdG9yKGR0cDIkR2VuZWlkLA0KICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGRkYXRhJGxhYmVscyRsYWJlbCkNCg0Kb2Zmc2V0LnNpemUgPC0gNA0KDQpwMSA8LSBnZ3Bsb3QoZGF0YSA9IGR0cDIpICsNCiAgY29vcmRfcG9sYXIoInkiLA0KICAgICAgICAgICAgICBzdGFydCA9IC0wLjMsDQogICAgICAgICAgICAgIGRpcmVjdGlvbiA9IC0xKSArDQogIGdlb21fdGlsZShhZXMoeCA9ICBhcy5udW1lcmljKENvbXBhcmlzb24pLA0KICAgICAgICAgICAgICAgIHkgPSBHZW5laWQsIA0KICAgICAgICAgICAgICAgIGZpbGwgPSBgR2VuZSBFeHByZXNzaW9uIERpZmZgKSwNCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IGR0cDJbQ29tcGFyaXNvbiA9PSAiQ29udHJvbCB2cy4gVVZCIiwgXSwNCiAgICAgICAgICAgIGFlcyh4ID0gcmVwKDEuNzUsDQogICAgICAgICAgICAgICAgICAgICAgICBubGV2ZWxzKEdlbmVpZCkpLA0KICAgICAgICAgICAgICAgIHkgPSBHZW5laWQsDQogICAgICAgICAgICAgICAgYW5nbGUgPSA5MCArIHNlcShmcm9tID0gMzAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IDMzMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBubGV2ZWxzKEdlbmVpZCkpW2FzLm51bWVyaWMoR2VuZWlkKV0gKyANCiAgICAgICAgICAgICAgICAgIG9mZnNldC5zaXplLA0KICAgICAgICAgICAgICAgIGxhYmVsID0gdW5pcXVlKEdlbmVpZCkpLA0KICAgICAgICAgICAgaGp1c3QgPSAwKSArDQogIGdlb21fdGV4dChkYXRhID0gZHRwMltHZW5laWQgPT0gbGV2ZWxzKGR0cDIkR2VuZWlkKVsxXSwgXSwNCiAgICAgICAgICAgIGFlcyh4ID0gMTpubGV2ZWxzKENvbXBhcmlzb24pLA0KICAgICAgICAgICAgICAgIHkgPSByZXAoLW9mZnNldC5zaXplLA0KICAgICAgICAgICAgICAgICAgICAgICAgbmxldmVscyhDb21wYXJpc29uKSksDQogICAgICAgICAgICAgICAgYW5nbGUgPSAwLA0KICAgICAgICAgICAgICAgIGxhYmVsID0gbGV2ZWxzKENvbXBhcmlzb24pKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMSwNCiAgICAgICAgICAgIHNpemUgPSA1KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJyZWQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJncmVlbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSAiZ3JleSIsIA0KICAgICAgICAgICAgICAgICAgICAgICBtaWRwb2ludCA9IDAsIA0KICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgc2NhbGVfeV9kaXNjcmV0ZSgiIiwNCiAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBjKDAsIDApKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksDQogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsDQogICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDEsICJpbiIpLA0KICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMC4zLCAiaW4iKSkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YSA9IGR0cDEsDQogICAgICAgICAgICAgICBhZXMoeCA9IC1zcXJ0KHkpICsgMC41LA0KICAgICAgICAgICAgICAgICAgIHkgPSB4LCANCiAgICAgICAgICAgICAgICAgICB4ZW5kID0gLXNxcnQoeWVuZCkgKyAwLjUsDQogICAgICAgICAgICAgICAgICAgeWVuZCA9IHhlbmQpLA0KICAgICAgICAgICAgICAgc2l6ZSA9IDEpIA0KDQp0aWZmKGZpbGVuYW1lID0gInRtcC9za2luX3Vidl93MTVfc2ZuX2hpdG1hcF93aXRoX3BoeWxvLnRpZmYiLA0KICAgICBoZWlnaHQgPSA4LA0KICAgICB3aWR0aCA9IDgsDQogICAgIHVuaXRzID0gJ2luJywNCiAgICAgcmVzID0gNjAwLA0KICAgICBjb21wcmVzc2lvbiA9ICJsencrcCIpDQpwbG90KHAxKQ0KZ3JhcGhpY3Mub2ZmKCkNCg0KcHJpbnQocDEpDQpgYGANCg0KDQojIyBWZW5uIERpYWdyYW0sIFdlZWsgMTUNCmBgYHtyIHcxNS12ZW5uLCBmaWcuaGVpZ2h0PTYsZmlnLndpZHRoPTR9DQojIDEuIEN0cmwgdnMuIFVWQg0KIyBhZGp1c3RlZCBwLXZhbHVlIDwgMC4xDQojIExGQyA+IDAgKHVwKSAgICAgICA6IDE0NDksIDguNCUNCiMgTEZDIDwgMCAoZG93bikgICAgIDogMTQ4MSwgOC42JQ0KIyAyMyBnZW5lcyBkb3duLXVwLWRvd24NCg0KIyAyLiBTRk4rVVZCIHZzLiBVVkINCiMgYWRqdXN0ZWQgcC12YWx1ZSA8IDAuMQ0KIyBMRkMgPiAwICh1cCkgICAgICAgOiAyNywgMC4xNiUNCiMgTEZDIDwgMCAoZG93bikgICAgIDogOSwgMC4wNTIlDQojIDEzIGdlbnMgdXAtZG93bi11cA0KDQpwMSA8LSBnZ3Bsb3QoKSArDQogIGdlb21fY2lyY2xlKGFlcyh4MCA9IGMoMSwgMiwgMSwgMiksDQogICAgICAgICAgICAgICAgICB5MCA9IGMoMSwgMSwgNCwgNCksDQogICAgICAgICAgICAgICAgICByID0gcmVwKDEsIDQpLA0KICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoYygyLCAxLCAxLCAyKSkpLA0KICAgICAgICAgICAgICBzaXplID0gMikgKw0KICBnZW9tX3RleHQoYWVzKHggPSByZXAoYygwLjUsIDEuNSwgMi41KSwgMiksDQogICAgICAgICAgICAgICAgeSA9IHJlcChjKDEsIDQpLCBlYWNoID0gMyksDQogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQoYygyNywgOCwgOSwgMTQ0OSwgOCwgMTQ4MSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmlnLm1hcmsgPSAiLCIpKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JlZW4iLCAicmVkIikpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQp0aWZmKGZpbGVuYW1lID0gInRtcC9za2luX3Vidl9zZm5fdzJfdmVubi50aWZmIiwNCiAgICAgaGVpZ2h0ID0gNiwNCiAgICAgd2lkdGggPSA0LA0KICAgICB1bml0cyA9ICdpbicsDQogICAgIHJlcyA9IDYwMCwNCiAgICAgY29tcHJlc3Npb24gPSAibHp3K3AiKQ0KcGxvdChwMSkNCmdyYXBoaWNzLm9mZigpDQoNCnByaW50KHAxKQ0KYGBgDQoNCiMjIEludGVyYWN0aW9ucyB0ZXJtcw0KVGVzdHMgaWYgdGhlIGVmZmVjdCBvZiBOT1QgdHJlYXRpbmcgd2l0aCBVVkIgdnMuIHRyZWF0aW5nIHdpdGggVVZCIGlzIGRpZmZlcmVudCBhdCBXZWVrIDE1IGNvbXBhcmVkIHRvIFdlZWsgMjogICAgDQpgYGB7ciBkZXNlcTJfd2VlazJfd2VlazE1X3Jlc3VsdHNfaW50X2Nvbl91dmJ9DQpyZXNfaW50X2Nvbl91dmJfd2VlayA8LSByZXN1bHRzKGRkcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAidGltZTE1dy50cnRDT04iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMSkNCnJlc19pbnRfY29uX3V2Yl93ZWVrIDwtIHJlc19pbnRfY29uX3V2Yl93ZWVrW29yZGVyKHJlc19pbnRfY29uX3V2Yl93ZWVrJHBhZGosDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gRkFMU0UpLF0NCnByaW50KHJlc19pbnRfY29uX3V2Yl93ZWVrKQ0Kc3VtbWFyeShyZXNfaW50X2Nvbl91dmJfd2VlaykNCg0KIyBIb3cgbWFueSBhZGp1c3RlZCBwLXZhbHVlcyB3ZXJlIGxlc3MgdGhhbiAwLjA1Pw0Kc3VtKHJlc19pbnRfY29uX3V2Yl93ZWVrJHBhZGogPCAwLjEsIA0KICAgIG5hLnJtID0gVFJVRSkNCg0KIyBNQSBwbG90DQpwcmludChwbG90TUEocmVzX2ludF9jb25fdXZiX3dlZWssDQogICAgICAgICAgICAgbWFpbiA9ICIoQ29udHJvbCB2cy4gVVZCKSB4IFRJbWUgSW50ZXJhY3Rpb24iLA0KICAgICAgICAgICAgIGFscGhhID0gMC45KSkNCg0KYGBgDQoNClRlc3RzIGlmIHRoZSBlZmZlY3Qgb2YgdHJlYXRpbmcgd2l0aCBVVkIrU0ZOIHZzLiB0cmVhdGluZyB3aXRoIFVWQiBpcyBkaWZmZXJlbnQgYXQgV2VlayAxNSBjb21wYXJlZCB0byBXZWVrIDI6ICAgIA0KYGBge3IgZGVzZXEyX3dlZWsyX3dlZWsxNV9yZXN1bHRzX2ludF9zZm5fdXZifQ0KcmVzX2ludF9zZm5fdXZiX3dlZWsgPC0gcmVzdWx0cyhkZHMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gInRpbWUxNXcudHJ0U0ZOIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjEpDQpyZXNfaW50X3Nmbl91dmJfd2VlayA8LSByZXNfaW50X3Nmbl91dmJfd2Vla1tvcmRlcihyZXNfaW50X3Nmbl91dmJfd2VlayRwYWRqLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjcmVhc2luZyA9IEZBTFNFKSxdDQpwcmludChyZXNfaW50X3Nmbl91dmJfd2VlaykNCnN1bW1hcnkocmVzX2ludF9zZm5fdXZiX3dlZWspDQoNCiMgSG93IG1hbnkgYWRqdXN0ZWQgcC12YWx1ZXMgd2VyZSBsZXNzIHRoYW4gMC4wNT8NCnN1bShyZXNfaW50X3Nmbl91dmJfd2VlayRwYWRqIDwgMC4xLCANCiAgICBuYS5ybSA9IFRSVUUpDQoNCiMgTUEgcGxvdA0KcHJpbnQocGxvdE1BKHJlc19pbnRfc2ZuX3V2Yl93ZWVrKSkNCg0KIyBOT1RFOiBzYW1lIGFzIA0KIyByZXMgPC0gcmVzdWx0cyhkZHMsIA0KIyAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMDUpDQojIHJlcyA8LSByZXNbb3JkZXIocmVzJHBhZGosIGRlY3JlYXNpbmcgPSBGQUxTRSksXQ0KIyByZXMNCmBgYA0KDQoqKk5PVEUqKjogQnkgZGVmYXVsdCwgdGhlICoqcmVzdWx0cyhkZHMpKioqIHByaW50cyB0aGUgcmVzdWx0cyBmb3IgdGhlIGxhc3QgbGV2ZWwgb2YgdGhlIGxhc3QgdGVybSwgaS5lLiBoZXJlIGl0IHdhcyBmb3IgZm9yIHRoZSBpbnRlcmFjdGlvbiB0ZXJtIFNGTiB2cy4gVVZCIGF0IFdlZWsgMTUgdnMuIFdlZWsgMi4NCiAgICAgICANCiMgR2VuZXMgd2l0aCBib3RoIGludGVyYWN0aW9ucyBiZWluZyBzaWduaWZpY2FudA0KYGBge3Igc2lnbl9pbnR9DQpsZ2VuZS5jb24gPC0gdW5pcXVlKHJlc19pbnRfY29uX3V2Yl93ZWVrQHJvd25hbWVzW3Jlc19pbnRfY29uX3V2Yl93ZWVrJHBhZGogPCAwLjFdKQ0KbGdlbmUuc2ZuIDwtIHVuaXF1ZShyZXNfaW50X3Nmbl91dmJfd2Vla0Byb3duYW1lc1tyZXNfaW50X3Nmbl91dmJfd2VlayRwYWRqIDwgMC4xXSkNCmxnZW5lIDwtIGxnZW5lLmNvbltsZ2VuZS5jb24gJWluJSBsZ2VuZS5zZm5dDQpsZ2VuZSA8LSBsZ2VuZVshaXMubmEobGdlbmUpXQ0KbGdlbmUNCmBgYA0KDQogICAgICAgDQpQbG90IG9mIERFU2VxLW5vcm1hbGl6ZWRjb3VudHMgb2YgZ2VuZXMgd2l0aCBzbWFsbGVzdCBhZGp1c3RlZCBwLXZhbHVlIGZvciB0aGUgaW50ZXJhY3Rpb24gdGVybTogICAgIA0KYGBge3IgZGVzZXEyX3dlZWsyX3dlZWsxNV90b3A5X2Rlc2Vxbm9ybSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDh9DQojIEdldCB0aGUgREVTZXEtbm9ybWFsaXplIGNvdW50cw0KZHAxIDwtIGxpc3QoKQ0KZm9yIChpIGluIDE6bGVuZ3RoKGxnZW5lKSkgew0KICBvdXQgPC0gcGxvdENvdW50cyhkZHMsIA0KICAgICAgICAgICAgICAgICAgICBnZW5lID0gbGdlbmVbW2ldXSwNCiAgICAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSBjKCJ0cnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUiKSwNCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuRGF0YSA9IFRSVUUpDQogIGRwMVtbaV1dIDwtIGRhdGEudGFibGUoR2VuZWlkID0gbGdlbmVbW2ldXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGUgPSByb3duYW1lcyhvdXQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG91dCkNCn0NCmRwMSA8LSByYmluZGxpc3QoZHAxKQ0KZHAxJHRydCA8LSBmYWN0b3IoZHAxJHRydCwNCiAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkNPTiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVVkIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0ZOIikpDQpkcDEkdGltZSA8LSBmYWN0b3IoZHAxJHRpbWUsDQogICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiMDJ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxNXciKSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJXZWVrIDIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldlZWsgMTUiKSkNCmRwMSRHZW5laWQgPC0gZmFjdG9yKGRwMSRHZW5laWQsDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZ2VuZSkNCmRwMVssIG11IDo9IG1lYW4oY291bnQsDQogICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSksDQogICAgYnkgPSBjKCJHZW5laWQiLA0KICAgICAgICAgICAidHJ0IiwNCiAgICAgICAgICAgInRpbWUiKV0NCmRtdSA8LSB1bmlxdWUoZHAxWywgLWMoIlNhbXBsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICJjb3VudCIpXSkNCg0KcDEgPC0gZ2dwbG90KGRwMSwNCiAgICAgICAgICAgICBhZXMoeCA9IHRpbWUsDQogICAgICAgICAgICAgICAgIHkgPSBjb3VudCwNCiAgICAgICAgICAgICAgICAgZ3JvdXAgPSB0cnQsDQogICAgICAgICAgICAgICAgIGZpbGwgPSB0cnQpKSArDQogIGZhY2V0X3dyYXAofiBHZW5laWQsDQogICAgICAgICAgICAgc2NhbGUgPSAiZnJlZV95IikgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC41KSwNCiAgICAgICAgICAgICBzaGFwZSA9IDIxLA0KICAgICAgICAgICAgIHNpemUgPSA1LA0KICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGRtdSwNCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwNCiAgICAgICAgICAgICAgICB5ID0gbXUsDQogICAgICAgICAgICAgICAgZ3JvdXAgPSB0cnQsDQogICAgICAgICAgICAgICAgY29sb3VyID0gdHJ0KSwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC41KSwNCiAgICAgICAgICAgIGFscGhhID0gMC41LA0KICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZSgiIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoIkRFU2VxLU5vcm1hbGl6ZWQgQ291bnRzIikgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKCJUcmVhdG1lbnQiKQ0KcHJpbnQocDEpDQpgYGANCiAgICAgIA0KQ29tcGFyZSB0byB0aGUgcGxvdCBvZiBUUE0tbm9ybWFsaXplZGNvdW50cyBvZiBnZW5lcyB3aXRoIHNtYWxsZXN0IGFkanVzdGVkIHAtdmFsdWUgZm9yIHRoZSBpbnRlcmFjdGlvbiB0ZXJtOiAgICAgDQpgYGB7ciBkZXNlcTJfd2VlazJfd2VlazE1X3RwbW5vcm0sIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4fQ0KIyBFeGFtaW5lIFRQTSB2YWx1ZXMgZm9yIHRoZSBzYW1lIGdlbmVzDQp0bXAgPC0gdHBtW0dlbmVpZCAlaW4lIGxnZW5lLCBdDQp0bXAkR2VuZWlkIDwtIGZhY3Rvcih0bXAkR2VuZWlkLA0KICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbGdlbmUpDQp0bXAgPC0gbWVsdC5kYXRhLnRhYmxlKGRhdGEgPSB0bXAsDQogICAgICAgICAgICAgICAgICAgICAgIGlkLnZhcnMgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSAzOm5jb2wodG1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJTYW1wbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIlRQTSIpDQp0bXAgPC0gbWVyZ2UoZG1ldGEsDQogICAgICAgICAgICAgdG1wLA0KICAgICAgICAgICAgIGJ5ID0gIlNhbXBsZSIpDQoNCnAxIDwtIGdncGxvdCh0bXAsDQogICAgICAgICAgICAgYWVzKHggPSBXZWVrLA0KICAgICAgICAgICAgICAgICB5ID0gVFBNLA0KICAgICAgICAgICAgICAgICBmaWxsID0gVHJlYXRtZW50LA0KICAgICAgICAgICAgICAgICBncm91cCA9IFRyZWF0bWVudCkpICsNCiAgZmFjZXRfd3JhcCh+IEdlbmVpZCwNCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV95IikgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC41KSwNCiAgICAgICAgICAgICBzaGFwZSA9IDIxLA0KICAgICAgICAgICAgIHNpemUgPSA1LA0KICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikrDQogIHNjYWxlX3hfZGlzY3JldGUoIiIpDQpwbG90KHAxKQ0KYGBgDQoNCiMgU2Vzc2lvbiBJbmZvcm1hdGlvbg0KYGBge3IgaW5mbyxldmFsPVRSVUV9DQpzZXNzaW9uSW5mbygpDQpgYGA=